From b285192a43f0432d82c2c10974204e78af0da596 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 14 Aug 2012 12:13:30 -0300 Subject: [media] rename most media/video pci drivers to media/pci Rename all PCI drivers with their own directory under drivers/media/video into drivers/media/pci and update the building system. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/saa7134/Kconfig | 64 + drivers/media/pci/saa7134/Makefile | 16 + drivers/media/pci/saa7134/saa6752hs.c | 1012 ++++ drivers/media/pci/saa7134/saa7134-alsa.c | 1209 ++++ drivers/media/pci/saa7134/saa7134-cards.c | 8026 +++++++++++++++++++++++++++ drivers/media/pci/saa7134/saa7134-core.c | 1368 +++++ drivers/media/pci/saa7134/saa7134-dvb.c | 1936 +++++++ drivers/media/pci/saa7134/saa7134-empress.c | 590 ++ drivers/media/pci/saa7134/saa7134-i2c.c | 435 ++ drivers/media/pci/saa7134/saa7134-input.c | 1041 ++++ drivers/media/pci/saa7134/saa7134-reg.h | 378 ++ drivers/media/pci/saa7134/saa7134-ts.c | 327 ++ drivers/media/pci/saa7134/saa7134-tvaudio.c | 1087 ++++ drivers/media/pci/saa7134/saa7134-vbi.c | 255 + drivers/media/pci/saa7134/saa7134-video.c | 2661 +++++++++ drivers/media/pci/saa7134/saa7134.h | 855 +++ 16 files changed, 21260 insertions(+) create mode 100644 drivers/media/pci/saa7134/Kconfig create mode 100644 drivers/media/pci/saa7134/Makefile create mode 100644 drivers/media/pci/saa7134/saa6752hs.c create mode 100644 drivers/media/pci/saa7134/saa7134-alsa.c create mode 100644 drivers/media/pci/saa7134/saa7134-cards.c create mode 100644 drivers/media/pci/saa7134/saa7134-core.c create mode 100644 drivers/media/pci/saa7134/saa7134-dvb.c create mode 100644 drivers/media/pci/saa7134/saa7134-empress.c create mode 100644 drivers/media/pci/saa7134/saa7134-i2c.c create mode 100644 drivers/media/pci/saa7134/saa7134-input.c create mode 100644 drivers/media/pci/saa7134/saa7134-reg.h create mode 100644 drivers/media/pci/saa7134/saa7134-ts.c create mode 100644 drivers/media/pci/saa7134/saa7134-tvaudio.c create mode 100644 drivers/media/pci/saa7134/saa7134-vbi.c create mode 100644 drivers/media/pci/saa7134/saa7134-video.c create mode 100644 drivers/media/pci/saa7134/saa7134.h (limited to 'drivers/media/pci/saa7134') diff --git a/drivers/media/pci/saa7134/Kconfig b/drivers/media/pci/saa7134/Kconfig new file mode 100644 index 00000000000..39fc0187a74 --- /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 VIDEO_HELPER_CHIPS_AUTO + ---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 !DVB_FE_CUSTOMISE + select DVB_MT352 if !DVB_FE_CUSTOMISE + select DVB_TDA1004X if !DVB_FE_CUSTOMISE + select DVB_NXT200X if !DVB_FE_CUSTOMISE + select DVB_TDA10086 if !DVB_FE_CUSTOMISE + select DVB_TDA826X if !DVB_FE_CUSTOMISE + select DVB_ISL6421 if !DVB_FE_CUSTOMISE + select DVB_ISL6405 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE + select DVB_ZL10036 if !DVB_FE_CUSTOMISE + select DVB_MT312 if !DVB_FE_CUSTOMISE + select DVB_LNBP21 if !DVB_FE_CUSTOMISE + select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select DVB_LGDT3305 if !DVB_FE_CUSTOMISE + select DVB_TDA10048 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE + select DVB_ZL10039 if !DVB_FE_CUSTOMISE + ---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 00000000000..aba50088dcd --- /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/video +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 00000000000..f147b05bd86 --- /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 + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 00000000000..10460fd3ce3 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 00000000000..bc08f1dbc29 --- /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 [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 +#include +#include +#include + +#include "saa7134-reg.h" +#include "saa7134.h" +#include "tuner-xc2028.h" +#include +#include +#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" */ + .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" */ + .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" */ + .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 */ + .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 */ + .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" */ + .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" */ + .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 */ + .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" */ + .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" */ + .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 */ + .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 */ + .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 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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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" ;-) */ + .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 + Lots of thanks to Andrey Zolotarev + */ + .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 */ + .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 */ + .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 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 + */ + .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 + */ + .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 */ + .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] = { + /* , Sergey */ + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov */ + .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 */ + /* Andrey Cvetcov */ + .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 + * 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 */ + /* Pavel Mihaylov */ + .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)" */ + .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 */ + .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 */ + .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 */ + /* 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 */ + .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" */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + /* David Bentham */ + .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 + Juan Pablo Sormani */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + /* Andrey Melnikoff */ + /* Beholder Intl. Ltd. Dmitry Belimov */ + /* Alexey Osipov */ + .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 */ + /* Andrey Melnikoff */ + /* Beholder Intl. Ltd. Dmitry Belimov */ + .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 */ + /* Andrey Melnikoff */ + /* Beholder Intl. Ltd. Dmitry Belimov */ + /* Alexey Osipov */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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= 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 00000000000..5fbb4e49495 --- /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 [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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "saa7134-reg.h" +#include "saa7134.h" + +MODULE_DESCRIPTION("v4l2 driver module for saa7130/34 based TV cards"); +MODULE_AUTHOR("Gerd Knorr [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_sync(&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: \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: \n" + "saa7134: I feel better now. Ok, here are the good news:\n" + "saa7134: You can use the card= 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=\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 00000000000..b209de40a4f --- /dev/null +++ b/drivers/media/pci/saa7134/saa7134-dvb.c @@ -0,0 +1,1936 @@ +/* + * + * (c) 2004 Gerd Knorr [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 +#include +#include +#include +#include +#include +#include + +#include "saa7134-reg.h" +#include "saa7134.h" +#include +#include "dvb-pll.h" +#include + +#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 [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 00000000000..dde361a9194 --- /dev/null +++ b/drivers/media/pci/saa7134/saa7134-empress.c @@ -0,0 +1,590 @@ +/* + * + * (c) 2004 Gerd Knorr [SuSE Labs] + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 +#include +#include +#include +#include + +#include "saa7134-reg.h" +#include "saa7134.h" + +#include +#include +#include + +/* ------------------------------------------------------------------ */ + +MODULE_AUTHOR("Gerd Knorr [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_sync(&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 00000000000..a176ec3285e --- /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 [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 +#include +#include +#include +#include + +#include "saa7134-reg.h" +#include "saa7134.h" +#include + +/* ----------------------------------------------------------- */ + +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 00000000000..0f78f5e537e --- /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 +#include +#include +#include +#include + +#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; + + /* 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; + + /* 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 + */ +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 + */ +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 00000000000..e7e0af101fa --- /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 00000000000..2e3f4b412d8 --- /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 [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 +#include +#include +#include +#include + +#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 00000000000..b7a99bee2f9 --- /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 [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 +#include +#include +#include +#include +#include +#include +#include + +#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 00000000000..e9aa94b807f --- /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 [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 +#include +#include +#include + +#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 00000000000..6de10b1e725 --- /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 [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 +#include +#include +#include +#include +#include + +#include "saa7134-reg.h" +#include "saa7134.h" +#include +#include + +/* ------------------------------------------------------------------ */ + +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, struct v4l2_crop *crop) +{ + struct saa7134_fh *fh = f; + struct saa7134_dev *dev = fh->dev; + struct v4l2_rect *b = &dev->crop_bounds; + + 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; + + if (crop->c.top < b->top) + crop->c.top = b->top; + if (crop->c.top > b->top + b->height) + crop->c.top = b->top + b->height; + if (crop->c.height > b->top - crop->c.top + b->height) + crop->c.height = b->top - crop->c.top + b->height; + + if (crop->c.left < b->left) + crop->c.left = b->left; + if (crop->c.left > b->left + b->width) + crop->c.left = b->left + b->width; + if (crop->c.width > b->left - crop->c.left + b->width) + crop->c.width = b->left - crop->c.left + b->width; + + dev->crop_current = crop->c; + 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, 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, + 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, + 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 00000000000..c24b6512bd8 --- /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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_VIDEO_SAA7134_DVB) || defined(CONFIG_VIDEO_SAA7134_DVB_MODULE) +#include +#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 -- cgit v1.2.3-70-g09d2 From cb7a01ac324bf2ee2c666f37ac867e4135f9785a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 14 Aug 2012 16:23:43 -0300 Subject: [media] move i2c files into drivers/media/i2c Move ancillary I2C drivers into drivers/media/i2c, in order to better organize them. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/Kconfig | 9 +- drivers/media/Makefile | 2 +- drivers/media/i2c/Kconfig | 566 +++ drivers/media/i2c/Makefile | 63 + drivers/media/i2c/adp1653.c | 487 +++ drivers/media/i2c/adv7170.c | 410 ++ drivers/media/i2c/adv7175.c | 460 ++ drivers/media/i2c/adv7180.c | 667 +++ drivers/media/i2c/adv7183.c | 699 ++++ drivers/media/i2c/adv7183_regs.h | 107 + drivers/media/i2c/adv7343.c | 476 +++ drivers/media/i2c/adv7343_regs.h | 181 + drivers/media/i2c/adv7393.c | 487 +++ drivers/media/i2c/adv7393_regs.h | 188 + drivers/media/i2c/ak881x.c | 359 ++ drivers/media/i2c/aptina-pll.c | 173 + drivers/media/i2c/aptina-pll.h | 56 + drivers/media/i2c/as3645a.c | 888 ++++ drivers/media/i2c/bt819.c | 517 +++ drivers/media/i2c/bt856.c | 273 ++ drivers/media/i2c/bt866.c | 243 ++ drivers/media/i2c/btcx-risc.c | 260 ++ drivers/media/i2c/btcx-risc.h | 34 + drivers/media/i2c/cs5345.c | 252 ++ drivers/media/i2c/cs53l32a.c | 251 ++ drivers/media/i2c/cx2341x.c | 1726 ++++++++ drivers/media/i2c/cx25840/Kconfig | 8 + drivers/media/i2c/cx25840/Makefile | 6 + drivers/media/i2c/cx25840/cx25840-audio.c | 571 +++ drivers/media/i2c/cx25840/cx25840-core.c | 5340 ++++++++++++++++++++++++ drivers/media/i2c/cx25840/cx25840-core.h | 137 + drivers/media/i2c/cx25840/cx25840-firmware.c | 175 + drivers/media/i2c/cx25840/cx25840-ir.c | 1281 ++++++ drivers/media/i2c/cx25840/cx25840-vbi.c | 256 ++ drivers/media/i2c/ir-kbd-i2c.c | 489 +++ drivers/media/i2c/ks0127.c | 724 ++++ drivers/media/i2c/ks0127.h | 51 + drivers/media/i2c/m52790.c | 216 + drivers/media/i2c/m5mols/Kconfig | 6 + drivers/media/i2c/m5mols/Makefile | 3 + drivers/media/i2c/m5mols/m5mols.h | 334 ++ drivers/media/i2c/m5mols/m5mols_capture.c | 155 + drivers/media/i2c/m5mols/m5mols_controls.c | 628 +++ drivers/media/i2c/m5mols/m5mols_core.c | 990 +++++ drivers/media/i2c/m5mols/m5mols_reg.h | 362 ++ drivers/media/i2c/msp3400-driver.c | 899 ++++ drivers/media/i2c/msp3400-driver.h | 137 + drivers/media/i2c/msp3400-kthreads.c | 1165 ++++++ drivers/media/i2c/mt9m032.c | 878 ++++ drivers/media/i2c/mt9p031.c | 1071 +++++ drivers/media/i2c/mt9t001.c | 833 ++++ drivers/media/i2c/mt9v011.c | 712 ++++ drivers/media/i2c/mt9v032.c | 763 ++++ drivers/media/i2c/noon010pc30.c | 851 ++++ drivers/media/i2c/ov7670.c | 1586 +++++++ drivers/media/i2c/s5k6aa.c | 1667 ++++++++ drivers/media/i2c/saa6588.c | 542 +++ drivers/media/i2c/saa7110.c | 494 +++ drivers/media/i2c/saa7115.c | 1727 ++++++++ drivers/media/i2c/saa711x_regs.h | 549 +++ drivers/media/i2c/saa7127.c | 852 ++++ drivers/media/i2c/saa717x.c | 1378 ++++++ drivers/media/i2c/saa7185.c | 377 ++ drivers/media/i2c/saa7191.c | 659 +++ drivers/media/i2c/saa7191.h | 245 ++ drivers/media/i2c/smiapp-pll.c | 418 ++ drivers/media/i2c/smiapp-pll.h | 103 + drivers/media/i2c/smiapp/Kconfig | 7 + drivers/media/i2c/smiapp/Makefile | 5 + drivers/media/i2c/smiapp/smiapp-core.c | 2895 +++++++++++++ drivers/media/i2c/smiapp/smiapp-limits.c | 132 + drivers/media/i2c/smiapp/smiapp-limits.h | 128 + drivers/media/i2c/smiapp/smiapp-quirk.c | 306 ++ drivers/media/i2c/smiapp/smiapp-quirk.h | 83 + drivers/media/i2c/smiapp/smiapp-reg-defs.h | 503 +++ drivers/media/i2c/smiapp/smiapp-reg.h | 122 + drivers/media/i2c/smiapp/smiapp-regs.c | 273 ++ drivers/media/i2c/smiapp/smiapp-regs.h | 49 + drivers/media/i2c/smiapp/smiapp.h | 252 ++ drivers/media/i2c/sr030pc30.c | 871 ++++ drivers/media/i2c/tcm825x.c | 937 +++++ drivers/media/i2c/tcm825x.h | 200 + drivers/media/i2c/tda7432.c | 485 +++ drivers/media/i2c/tda9840.c | 224 + drivers/media/i2c/tea6415c.c | 187 + drivers/media/i2c/tea6415c.h | 27 + drivers/media/i2c/tea6420.c | 169 + drivers/media/i2c/tea6420.h | 24 + drivers/media/i2c/ths7303.c | 140 + drivers/media/i2c/tlv320aic23b.c | 230 + drivers/media/i2c/tvaudio.c | 2118 ++++++++++ drivers/media/i2c/tveeprom.c | 792 ++++ drivers/media/i2c/tvp514x.c | 1166 ++++++ drivers/media/i2c/tvp514x_regs.h | 287 ++ drivers/media/i2c/tvp5150.c | 1274 ++++++ drivers/media/i2c/tvp5150_reg.h | 139 + drivers/media/i2c/tvp7002.c | 1145 +++++ drivers/media/i2c/tvp7002_reg.h | 150 + drivers/media/i2c/upd64031a.c | 274 ++ drivers/media/i2c/upd64083.c | 246 ++ drivers/media/i2c/vp27smpx.c | 211 + drivers/media/i2c/vpx3220.c | 591 +++ drivers/media/i2c/vs6624.c | 928 ++++ drivers/media/i2c/vs6624_regs.h | 337 ++ drivers/media/i2c/wm8739.c | 294 ++ drivers/media/i2c/wm8775.c | 342 ++ drivers/media/pci/bt8xx/Makefile | 2 +- drivers/media/pci/cx23885/Makefile | 2 +- drivers/media/pci/cx25821/Makefile | 2 +- drivers/media/pci/cx88/Makefile | 2 +- drivers/media/pci/ivtv/Makefile | 2 +- drivers/media/pci/saa7134/Makefile | 2 +- drivers/media/pci/saa7146/Makefile | 2 +- drivers/media/pci/saa7164/Makefile | 2 +- drivers/media/usb/cx231xx/Makefile | 2 +- drivers/media/usb/em28xx/Makefile | 2 +- drivers/media/usb/hdpvr/Makefile | 2 +- drivers/media/usb/pvrusb2/Makefile | 2 +- drivers/media/usb/stk1160/Makefile | 2 +- drivers/media/usb/tlg2300/Makefile | 2 +- drivers/media/usb/tm6000/Makefile | 2 +- drivers/media/usb/usbvision/Makefile | 2 +- drivers/media/video/Kconfig | 579 +-- drivers/media/video/Makefile | 71 - drivers/media/video/adp1653.c | 487 --- drivers/media/video/adv7170.c | 410 -- drivers/media/video/adv7175.c | 460 -- drivers/media/video/adv7180.c | 667 --- drivers/media/video/adv7183.c | 699 ---- drivers/media/video/adv7183_regs.h | 107 - drivers/media/video/adv7343.c | 476 --- drivers/media/video/adv7343_regs.h | 181 - drivers/media/video/adv7393.c | 487 --- drivers/media/video/adv7393_regs.h | 188 - drivers/media/video/ak881x.c | 359 -- drivers/media/video/aptina-pll.c | 173 - drivers/media/video/aptina-pll.h | 56 - drivers/media/video/as3645a.c | 888 ---- drivers/media/video/bt819.c | 517 --- drivers/media/video/bt856.c | 273 -- drivers/media/video/bt866.c | 243 -- drivers/media/video/btcx-risc.c | 260 -- drivers/media/video/btcx-risc.h | 34 - drivers/media/video/cs5345.c | 252 -- drivers/media/video/cs53l32a.c | 251 -- drivers/media/video/cx2341x.c | 1726 -------- drivers/media/video/cx25840/Kconfig | 8 - drivers/media/video/cx25840/Makefile | 6 - drivers/media/video/cx25840/cx25840-audio.c | 571 --- drivers/media/video/cx25840/cx25840-core.c | 5340 ------------------------ drivers/media/video/cx25840/cx25840-core.h | 137 - drivers/media/video/cx25840/cx25840-firmware.c | 175 - drivers/media/video/cx25840/cx25840-ir.c | 1281 ------ drivers/media/video/cx25840/cx25840-vbi.c | 256 -- drivers/media/video/ir-kbd-i2c.c | 489 --- drivers/media/video/ks0127.c | 724 ---- drivers/media/video/ks0127.h | 51 - drivers/media/video/m52790.c | 216 - drivers/media/video/m5mols/Kconfig | 6 - drivers/media/video/m5mols/Makefile | 3 - drivers/media/video/m5mols/m5mols.h | 334 -- drivers/media/video/m5mols/m5mols_capture.c | 155 - drivers/media/video/m5mols/m5mols_controls.c | 628 --- drivers/media/video/m5mols/m5mols_core.c | 990 ----- drivers/media/video/m5mols/m5mols_reg.h | 362 -- drivers/media/video/msp3400-driver.c | 899 ---- drivers/media/video/msp3400-driver.h | 137 - drivers/media/video/msp3400-kthreads.c | 1165 ------ drivers/media/video/mt9m032.c | 878 ---- drivers/media/video/mt9p031.c | 1071 ----- drivers/media/video/mt9t001.c | 833 ---- drivers/media/video/mt9v011.c | 712 ---- drivers/media/video/mt9v032.c | 763 ---- drivers/media/video/noon010pc30.c | 851 ---- drivers/media/video/ov7670.c | 1586 ------- drivers/media/video/s5k6aa.c | 1667 -------- drivers/media/video/saa6588.c | 542 --- drivers/media/video/saa7110.c | 494 --- drivers/media/video/saa7115.c | 1727 -------- drivers/media/video/saa711x_regs.h | 549 --- drivers/media/video/saa7127.c | 852 ---- drivers/media/video/saa717x.c | 1378 ------ drivers/media/video/saa7185.c | 377 -- drivers/media/video/saa7191.c | 659 --- drivers/media/video/saa7191.h | 245 -- drivers/media/video/smiapp-pll.c | 418 -- drivers/media/video/smiapp-pll.h | 103 - drivers/media/video/smiapp/Kconfig | 7 - drivers/media/video/smiapp/Makefile | 5 - drivers/media/video/smiapp/smiapp-core.c | 2895 ------------- drivers/media/video/smiapp/smiapp-limits.c | 132 - drivers/media/video/smiapp/smiapp-limits.h | 128 - drivers/media/video/smiapp/smiapp-quirk.c | 306 -- drivers/media/video/smiapp/smiapp-quirk.h | 83 - drivers/media/video/smiapp/smiapp-reg-defs.h | 503 --- drivers/media/video/smiapp/smiapp-reg.h | 122 - drivers/media/video/smiapp/smiapp-regs.c | 273 -- drivers/media/video/smiapp/smiapp-regs.h | 49 - drivers/media/video/smiapp/smiapp.h | 252 -- drivers/media/video/sr030pc30.c | 871 ---- drivers/media/video/tcm825x.c | 937 ----- drivers/media/video/tcm825x.h | 200 - drivers/media/video/tda7432.c | 485 --- drivers/media/video/tda9840.c | 224 - drivers/media/video/tea6415c.c | 187 - drivers/media/video/tea6415c.h | 27 - drivers/media/video/tea6420.c | 169 - drivers/media/video/tea6420.h | 24 - drivers/media/video/ths7303.c | 140 - drivers/media/video/tlv320aic23b.c | 230 - drivers/media/video/tvaudio.c | 2118 ---------- drivers/media/video/tveeprom.c | 792 ---- drivers/media/video/tvp514x.c | 1166 ------ drivers/media/video/tvp514x_regs.h | 287 -- drivers/media/video/tvp5150.c | 1274 ------ drivers/media/video/tvp5150_reg.h | 139 - drivers/media/video/tvp7002.c | 1145 ----- drivers/media/video/tvp7002_reg.h | 150 - drivers/media/video/upd64031a.c | 274 -- drivers/media/video/upd64083.c | 246 -- drivers/media/video/vp27smpx.c | 211 - drivers/media/video/vpx3220.c | 591 --- drivers/media/video/vs6624.c | 928 ---- drivers/media/video/vs6624_regs.h | 337 -- drivers/media/video/wm8739.c | 294 -- drivers/media/video/wm8775.c | 342 -- 226 files changed, 58027 insertions(+), 58045 deletions(-) create mode 100644 drivers/media/i2c/Kconfig create mode 100644 drivers/media/i2c/Makefile create mode 100644 drivers/media/i2c/adp1653.c create mode 100644 drivers/media/i2c/adv7170.c create mode 100644 drivers/media/i2c/adv7175.c create mode 100644 drivers/media/i2c/adv7180.c create mode 100644 drivers/media/i2c/adv7183.c create mode 100644 drivers/media/i2c/adv7183_regs.h create mode 100644 drivers/media/i2c/adv7343.c create mode 100644 drivers/media/i2c/adv7343_regs.h create mode 100644 drivers/media/i2c/adv7393.c create mode 100644 drivers/media/i2c/adv7393_regs.h create mode 100644 drivers/media/i2c/ak881x.c create mode 100644 drivers/media/i2c/aptina-pll.c create mode 100644 drivers/media/i2c/aptina-pll.h create mode 100644 drivers/media/i2c/as3645a.c create mode 100644 drivers/media/i2c/bt819.c create mode 100644 drivers/media/i2c/bt856.c create mode 100644 drivers/media/i2c/bt866.c create mode 100644 drivers/media/i2c/btcx-risc.c create mode 100644 drivers/media/i2c/btcx-risc.h create mode 100644 drivers/media/i2c/cs5345.c create mode 100644 drivers/media/i2c/cs53l32a.c create mode 100644 drivers/media/i2c/cx2341x.c create mode 100644 drivers/media/i2c/cx25840/Kconfig create mode 100644 drivers/media/i2c/cx25840/Makefile create mode 100644 drivers/media/i2c/cx25840/cx25840-audio.c create mode 100644 drivers/media/i2c/cx25840/cx25840-core.c create mode 100644 drivers/media/i2c/cx25840/cx25840-core.h create mode 100644 drivers/media/i2c/cx25840/cx25840-firmware.c create mode 100644 drivers/media/i2c/cx25840/cx25840-ir.c create mode 100644 drivers/media/i2c/cx25840/cx25840-vbi.c create mode 100644 drivers/media/i2c/ir-kbd-i2c.c create mode 100644 drivers/media/i2c/ks0127.c create mode 100644 drivers/media/i2c/ks0127.h create mode 100644 drivers/media/i2c/m52790.c create mode 100644 drivers/media/i2c/m5mols/Kconfig create mode 100644 drivers/media/i2c/m5mols/Makefile create mode 100644 drivers/media/i2c/m5mols/m5mols.h create mode 100644 drivers/media/i2c/m5mols/m5mols_capture.c create mode 100644 drivers/media/i2c/m5mols/m5mols_controls.c create mode 100644 drivers/media/i2c/m5mols/m5mols_core.c create mode 100644 drivers/media/i2c/m5mols/m5mols_reg.h create mode 100644 drivers/media/i2c/msp3400-driver.c create mode 100644 drivers/media/i2c/msp3400-driver.h create mode 100644 drivers/media/i2c/msp3400-kthreads.c create mode 100644 drivers/media/i2c/mt9m032.c create mode 100644 drivers/media/i2c/mt9p031.c create mode 100644 drivers/media/i2c/mt9t001.c create mode 100644 drivers/media/i2c/mt9v011.c create mode 100644 drivers/media/i2c/mt9v032.c create mode 100644 drivers/media/i2c/noon010pc30.c create mode 100644 drivers/media/i2c/ov7670.c create mode 100644 drivers/media/i2c/s5k6aa.c create mode 100644 drivers/media/i2c/saa6588.c create mode 100644 drivers/media/i2c/saa7110.c create mode 100644 drivers/media/i2c/saa7115.c create mode 100644 drivers/media/i2c/saa711x_regs.h create mode 100644 drivers/media/i2c/saa7127.c create mode 100644 drivers/media/i2c/saa717x.c create mode 100644 drivers/media/i2c/saa7185.c create mode 100644 drivers/media/i2c/saa7191.c create mode 100644 drivers/media/i2c/saa7191.h create mode 100644 drivers/media/i2c/smiapp-pll.c create mode 100644 drivers/media/i2c/smiapp-pll.h create mode 100644 drivers/media/i2c/smiapp/Kconfig create mode 100644 drivers/media/i2c/smiapp/Makefile create mode 100644 drivers/media/i2c/smiapp/smiapp-core.c create mode 100644 drivers/media/i2c/smiapp/smiapp-limits.c create mode 100644 drivers/media/i2c/smiapp/smiapp-limits.h create mode 100644 drivers/media/i2c/smiapp/smiapp-quirk.c create mode 100644 drivers/media/i2c/smiapp/smiapp-quirk.h create mode 100644 drivers/media/i2c/smiapp/smiapp-reg-defs.h create mode 100644 drivers/media/i2c/smiapp/smiapp-reg.h create mode 100644 drivers/media/i2c/smiapp/smiapp-regs.c create mode 100644 drivers/media/i2c/smiapp/smiapp-regs.h create mode 100644 drivers/media/i2c/smiapp/smiapp.h create mode 100644 drivers/media/i2c/sr030pc30.c create mode 100644 drivers/media/i2c/tcm825x.c create mode 100644 drivers/media/i2c/tcm825x.h create mode 100644 drivers/media/i2c/tda7432.c create mode 100644 drivers/media/i2c/tda9840.c create mode 100644 drivers/media/i2c/tea6415c.c create mode 100644 drivers/media/i2c/tea6415c.h create mode 100644 drivers/media/i2c/tea6420.c create mode 100644 drivers/media/i2c/tea6420.h create mode 100644 drivers/media/i2c/ths7303.c create mode 100644 drivers/media/i2c/tlv320aic23b.c create mode 100644 drivers/media/i2c/tvaudio.c create mode 100644 drivers/media/i2c/tveeprom.c create mode 100644 drivers/media/i2c/tvp514x.c create mode 100644 drivers/media/i2c/tvp514x_regs.h create mode 100644 drivers/media/i2c/tvp5150.c create mode 100644 drivers/media/i2c/tvp5150_reg.h create mode 100644 drivers/media/i2c/tvp7002.c create mode 100644 drivers/media/i2c/tvp7002_reg.h create mode 100644 drivers/media/i2c/upd64031a.c create mode 100644 drivers/media/i2c/upd64083.c create mode 100644 drivers/media/i2c/vp27smpx.c create mode 100644 drivers/media/i2c/vpx3220.c create mode 100644 drivers/media/i2c/vs6624.c create mode 100644 drivers/media/i2c/vs6624_regs.h create mode 100644 drivers/media/i2c/wm8739.c create mode 100644 drivers/media/i2c/wm8775.c delete mode 100644 drivers/media/video/adp1653.c delete mode 100644 drivers/media/video/adv7170.c delete mode 100644 drivers/media/video/adv7175.c delete mode 100644 drivers/media/video/adv7180.c delete mode 100644 drivers/media/video/adv7183.c delete mode 100644 drivers/media/video/adv7183_regs.h delete mode 100644 drivers/media/video/adv7343.c delete mode 100644 drivers/media/video/adv7343_regs.h delete mode 100644 drivers/media/video/adv7393.c delete mode 100644 drivers/media/video/adv7393_regs.h delete mode 100644 drivers/media/video/ak881x.c delete mode 100644 drivers/media/video/aptina-pll.c delete mode 100644 drivers/media/video/aptina-pll.h delete mode 100644 drivers/media/video/as3645a.c delete mode 100644 drivers/media/video/bt819.c delete mode 100644 drivers/media/video/bt856.c delete mode 100644 drivers/media/video/bt866.c delete mode 100644 drivers/media/video/btcx-risc.c delete mode 100644 drivers/media/video/btcx-risc.h delete mode 100644 drivers/media/video/cs5345.c delete mode 100644 drivers/media/video/cs53l32a.c delete mode 100644 drivers/media/video/cx2341x.c delete mode 100644 drivers/media/video/cx25840/Kconfig delete mode 100644 drivers/media/video/cx25840/Makefile delete mode 100644 drivers/media/video/cx25840/cx25840-audio.c delete mode 100644 drivers/media/video/cx25840/cx25840-core.c delete mode 100644 drivers/media/video/cx25840/cx25840-core.h delete mode 100644 drivers/media/video/cx25840/cx25840-firmware.c delete mode 100644 drivers/media/video/cx25840/cx25840-ir.c delete mode 100644 drivers/media/video/cx25840/cx25840-vbi.c delete mode 100644 drivers/media/video/ir-kbd-i2c.c delete mode 100644 drivers/media/video/ks0127.c delete mode 100644 drivers/media/video/ks0127.h delete mode 100644 drivers/media/video/m52790.c delete mode 100644 drivers/media/video/m5mols/Kconfig delete mode 100644 drivers/media/video/m5mols/Makefile delete mode 100644 drivers/media/video/m5mols/m5mols.h delete mode 100644 drivers/media/video/m5mols/m5mols_capture.c delete mode 100644 drivers/media/video/m5mols/m5mols_controls.c delete mode 100644 drivers/media/video/m5mols/m5mols_core.c delete mode 100644 drivers/media/video/m5mols/m5mols_reg.h delete mode 100644 drivers/media/video/msp3400-driver.c delete mode 100644 drivers/media/video/msp3400-driver.h delete mode 100644 drivers/media/video/msp3400-kthreads.c delete mode 100644 drivers/media/video/mt9m032.c delete mode 100644 drivers/media/video/mt9p031.c delete mode 100644 drivers/media/video/mt9t001.c delete mode 100644 drivers/media/video/mt9v011.c delete mode 100644 drivers/media/video/mt9v032.c delete mode 100644 drivers/media/video/noon010pc30.c delete mode 100644 drivers/media/video/ov7670.c delete mode 100644 drivers/media/video/s5k6aa.c delete mode 100644 drivers/media/video/saa6588.c delete mode 100644 drivers/media/video/saa7110.c delete mode 100644 drivers/media/video/saa7115.c delete mode 100644 drivers/media/video/saa711x_regs.h delete mode 100644 drivers/media/video/saa7127.c delete mode 100644 drivers/media/video/saa717x.c delete mode 100644 drivers/media/video/saa7185.c delete mode 100644 drivers/media/video/saa7191.c delete mode 100644 drivers/media/video/saa7191.h delete mode 100644 drivers/media/video/smiapp-pll.c delete mode 100644 drivers/media/video/smiapp-pll.h delete mode 100644 drivers/media/video/smiapp/Kconfig delete mode 100644 drivers/media/video/smiapp/Makefile delete mode 100644 drivers/media/video/smiapp/smiapp-core.c delete mode 100644 drivers/media/video/smiapp/smiapp-limits.c delete mode 100644 drivers/media/video/smiapp/smiapp-limits.h delete mode 100644 drivers/media/video/smiapp/smiapp-quirk.c delete mode 100644 drivers/media/video/smiapp/smiapp-quirk.h delete mode 100644 drivers/media/video/smiapp/smiapp-reg-defs.h delete mode 100644 drivers/media/video/smiapp/smiapp-reg.h delete mode 100644 drivers/media/video/smiapp/smiapp-regs.c delete mode 100644 drivers/media/video/smiapp/smiapp-regs.h delete mode 100644 drivers/media/video/smiapp/smiapp.h delete mode 100644 drivers/media/video/sr030pc30.c delete mode 100644 drivers/media/video/tcm825x.c delete mode 100644 drivers/media/video/tcm825x.h delete mode 100644 drivers/media/video/tda7432.c delete mode 100644 drivers/media/video/tda9840.c delete mode 100644 drivers/media/video/tea6415c.c delete mode 100644 drivers/media/video/tea6415c.h delete mode 100644 drivers/media/video/tea6420.c delete mode 100644 drivers/media/video/tea6420.h delete mode 100644 drivers/media/video/ths7303.c delete mode 100644 drivers/media/video/tlv320aic23b.c delete mode 100644 drivers/media/video/tvaudio.c delete mode 100644 drivers/media/video/tveeprom.c delete mode 100644 drivers/media/video/tvp514x.c delete mode 100644 drivers/media/video/tvp514x_regs.h delete mode 100644 drivers/media/video/tvp5150.c delete mode 100644 drivers/media/video/tvp5150_reg.h delete mode 100644 drivers/media/video/tvp7002.c delete mode 100644 drivers/media/video/tvp7002_reg.h delete mode 100644 drivers/media/video/upd64031a.c delete mode 100644 drivers/media/video/upd64083.c delete mode 100644 drivers/media/video/vp27smpx.c delete mode 100644 drivers/media/video/vpx3220.c delete mode 100644 drivers/media/video/vs6624.c delete mode 100644 drivers/media/video/vs6624_regs.h delete mode 100644 drivers/media/video/wm8739.c delete mode 100644 drivers/media/video/wm8775.c (limited to 'drivers/media/pci/saa7134') diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index c9cdc61e8b5..26f3de57a97 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -151,18 +151,15 @@ source "drivers/media/rc/Kconfig" source "drivers/media/tuners/Kconfig" +source "drivers/media/i2c/Kconfig" + # -# Video/Radio/Hybrid adapters +# V4L platform/mem2mem drivers # - source "drivers/media/video/Kconfig" source "drivers/media/radio/Kconfig" -# -# DVB adapters -# - source "drivers/media/pci/Kconfig" source "drivers/media/usb/Kconfig" source "drivers/media/mmc/Kconfig" diff --git a/drivers/media/Makefile b/drivers/media/Makefile index 360c44dec3e..e1be1961586 100644 --- a/drivers/media/Makefile +++ b/drivers/media/Makefile @@ -9,7 +9,7 @@ ifeq ($(CONFIG_MEDIA_CONTROLLER),y) endif obj-y += tuners/ common/ rc/ video/ -obj-y += pci/ usb/ mmc/ firewire/ parport/ +obj-y += i2c/ pci/ usb/ mmc/ firewire/ parport/ obj-$(CONFIG_VIDEO_DEV) += radio/ v4l2-core/ obj-$(CONFIG_DVB_CORE) += dvb-core/ dvb-frontends/ diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig new file mode 100644 index 00000000000..1c677f5e3a1 --- /dev/null +++ b/drivers/media/i2c/Kconfig @@ -0,0 +1,566 @@ +# +# Generic video config states +# + +config VIDEO_BTCX + depends on PCI + tristate + +config VIDEO_TVEEPROM + tristate + depends on I2C + +# +# Multimedia Video device configuration +# + +if VIDEO_V4L2 + +config VIDEO_HELPER_CHIPS_AUTO + bool "Autoselect pertinent encoders/decoders and other helper chips" + default y if !EXPERT + ---help--- + Most video cards may require additional modules to encode or + decode audio/video standards. This option will autoselect + all pertinent modules to each selected video module. + + Unselect this only if you know exactly what you are doing, since + it may break support on some boards. + + In doubt, say Y. + +config VIDEO_IR_I2C + tristate "I2C module for IR" if !VIDEO_HELPER_CHIPS_AUTO + depends on I2C && RC_CORE + default y + ---help--- + Most boards have an IR chip directly connected via GPIO. However, + some video boards have the IR connected via I2C bus. + + If your board doesn't have an I2C IR chip, you may disable this + option. + + In doubt, say Y. + +# +# Encoder / Decoder module configuration +# + +menu "Encoders, decoders, sensors and other helper chips" + visible if !VIDEO_HELPER_CHIPS_AUTO + +comment "Audio decoders, processors and mixers" + +config VIDEO_TVAUDIO + tristate "Simple audio decoder chips" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for several audio decoder chips found on some bt8xx boards: + Philips: tda9840, tda9873h, tda9874h/a, tda9850, tda985x, tea6300, + tea6320, tea6420, tda8425, ta8874z. + Microchip: pic16c54 based design on ProVideo PV951 board. + + To compile this driver as a module, choose M here: the + module will be called tvaudio. + +config VIDEO_TDA7432 + tristate "Philips TDA7432 audio processor" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for tda7432 audio decoder chip found on some bt8xx boards. + + To compile this driver as a module, choose M here: the + module will be called tda7432. + +config VIDEO_TDA9840 + tristate "Philips TDA9840 audio processor" + depends on I2C + ---help--- + Support for tda9840 audio decoder chip found on some Zoran boards. + + To compile this driver as a module, choose M here: the + module will be called tda9840. + +config VIDEO_TEA6415C + tristate "Philips TEA6415C audio processor" + depends on I2C + ---help--- + Support for tea6415c audio decoder chip found on some bt8xx boards. + + To compile this driver as a module, choose M here: the + module will be called tea6415c. + +config VIDEO_TEA6420 + tristate "Philips TEA6420 audio processor" + depends on I2C + ---help--- + Support for tea6420 audio decoder chip found on some bt8xx boards. + + To compile this driver as a module, choose M here: the + module will be called tea6420. + +config VIDEO_MSP3400 + tristate "Micronas MSP34xx audio decoders" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Micronas MSP34xx series of audio decoders. + + To compile this driver as a module, choose M here: the + module will be called msp3400. + +config VIDEO_CS5345 + tristate "Cirrus Logic CS5345 audio ADC" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Cirrus Logic CS5345 24-bit, 192 kHz + stereo A/D converter. + + To compile this driver as a module, choose M here: the + module will be called cs5345. + +config VIDEO_CS53L32A + tristate "Cirrus Logic CS53L32A audio ADC" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Cirrus Logic CS53L32A low voltage + stereo A/D converter. + + To compile this driver as a module, choose M here: the + module will be called cs53l32a. + +config VIDEO_TLV320AIC23B + tristate "Texas Instruments TLV320AIC23B audio codec" + depends on VIDEO_V4L2 && I2C && EXPERIMENTAL + ---help--- + Support for the Texas Instruments TLV320AIC23B audio codec. + + To compile this driver as a module, choose M here: the + module will be called tlv320aic23b. + +config VIDEO_WM8775 + tristate "Wolfson Microelectronics WM8775 audio ADC with input mixer" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Wolfson Microelectronics WM8775 high + performance stereo A/D Converter with a 4 channel input mixer. + + To compile this driver as a module, choose M here: the + module will be called wm8775. + +config VIDEO_WM8739 + tristate "Wolfson Microelectronics WM8739 stereo audio ADC" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Wolfson Microelectronics WM8739 + stereo A/D Converter. + + To compile this driver as a module, choose M here: the + module will be called wm8739. + +config VIDEO_VP27SMPX + tristate "Panasonic VP27s internal MPX" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the internal MPX of the Panasonic VP27s tuner. + + To compile this driver as a module, choose M here: the + module will be called vp27smpx. + +comment "RDS decoders" + +config VIDEO_SAA6588 + tristate "SAA6588 Radio Chip RDS decoder support" + depends on VIDEO_V4L2 && I2C + + help + Support for this Radio Data System (RDS) decoder. This allows + seeing radio station identification transmitted using this + standard. + + To compile this driver as a module, choose M here: the + module will be called saa6588. + +comment "Video decoders" + +config VIDEO_ADV7180 + tristate "Analog Devices ADV7180 decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Analog Devices ADV7180 video decoder. + + To compile this driver as a module, choose M here: the + module will be called adv7180. + +config VIDEO_ADV7183 + tristate "Analog Devices ADV7183 decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + V4l2 subdevice driver for the Analog Devices + ADV7183 video decoder. + + To compile this driver as a module, choose M here: the + module will be called adv7183. + +config VIDEO_BT819 + tristate "BT819A VideoStream decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for BT819A video decoder. + + To compile this driver as a module, choose M here: the + module will be called bt819. + +config VIDEO_BT856 + tristate "BT856 VideoStream decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for BT856 video decoder. + + To compile this driver as a module, choose M here: the + module will be called bt856. + +config VIDEO_BT866 + tristate "BT866 VideoStream decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for BT866 video decoder. + + To compile this driver as a module, choose M here: the + module will be called bt866. + +config VIDEO_KS0127 + tristate "KS0127 video decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for KS0127 video decoder. + + This chip is used on AverMedia AVS6EYES Zoran-based MJPEG + cards. + + To compile this driver as a module, choose M here: the + module will be called ks0127. + +config VIDEO_SAA7110 + tristate "Philips SAA7110 video decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Philips SAA7110 video decoders. + + To compile this driver as a module, choose M here: the + module will be called saa7110. + +config VIDEO_SAA711X + tristate "Philips SAA7111/3/4/5 video decoders" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Philips SAA7111/3/4/5 video decoders. + + To compile this driver as a module, choose M here: the + module will be called saa7115. + +config VIDEO_SAA7191 + tristate "Philips SAA7191 video decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Philips SAA7191 video decoder. + + To compile this driver as a module, choose M here: the + module will be called saa7191. + +config VIDEO_TVP514X + tristate "Texas Instruments TVP514x video decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + This is a Video4Linux2 sensor-level driver for the TI TVP5146/47 + decoder. It is currently working with the TI OMAP3 camera + controller. + + To compile this driver as a module, choose M here: the + module will be called tvp514x. + +config VIDEO_TVP5150 + tristate "Texas Instruments TVP5150 video decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Texas Instruments TVP5150 video decoder. + + To compile this driver as a module, choose M here: the + module will be called tvp5150. + +config VIDEO_TVP7002 + tristate "Texas Instruments TVP7002 video decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Texas Instruments TVP7002 video decoder. + + To compile this driver as a module, choose M here: the + module will be called tvp7002. + +config VIDEO_VPX3220 + tristate "vpx3220a, vpx3216b & vpx3214c video decoders" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for VPX322x video decoders. + + To compile this driver as a module, choose M here: the + module will be called vpx3220. + +comment "Video and audio decoders" + +config VIDEO_SAA717X + tristate "Philips SAA7171/3/4 audio/video decoders" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Philips SAA7171/3/4 audio/video decoders. + + To compile this driver as a module, choose M here: the + module will be called saa717x. + +source "drivers/media/i2c/cx25840/Kconfig" + +comment "MPEG video encoders" + +config VIDEO_CX2341X + tristate "Conexant CX2341x MPEG encoders" + depends on VIDEO_V4L2 && VIDEO_V4L2_COMMON + ---help--- + Support for the Conexant CX23416 MPEG encoders + and CX23415 MPEG encoder/decoders. + + This module currently supports the encoding functions only. + + To compile this driver as a module, choose M here: the + module will be called cx2341x. + +comment "Video encoders" + +config VIDEO_SAA7127 + tristate "Philips SAA7127/9 digital video encoders" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Philips SAA7127/9 digital video encoders. + + To compile this driver as a module, choose M here: the + module will be called saa7127. + +config VIDEO_SAA7185 + tristate "Philips SAA7185 video encoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Philips SAA7185 video encoder. + + To compile this driver as a module, choose M here: the + module will be called saa7185. + +config VIDEO_ADV7170 + tristate "Analog Devices ADV7170 video encoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Analog Devices ADV7170 video encoder driver + + To compile this driver as a module, choose M here: the + module will be called adv7170. + +config VIDEO_ADV7175 + tristate "Analog Devices ADV7175 video encoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Analog Devices ADV7175 video encoder driver + + To compile this driver as a module, choose M here: the + module will be called adv7175. + +config VIDEO_ADV7343 + tristate "ADV7343 video encoder" + depends on I2C + help + Support for Analog Devices I2C bus based ADV7343 encoder. + + To compile this driver as a module, choose M here: the + module will be called adv7343. + +config VIDEO_ADV7393 + tristate "ADV7393 video encoder" + depends on I2C + help + Support for Analog Devices I2C bus based ADV7393 encoder. + + To compile this driver as a module, choose M here: the + module will be called adv7393. + +config VIDEO_AK881X + tristate "AK8813/AK8814 video encoders" + depends on I2C + help + Video output driver for AKM AK8813 and AK8814 TV encoders + +comment "Camera sensor devices" + +config VIDEO_APTINA_PLL + tristate + +config VIDEO_SMIAPP_PLL + tristate + +config VIDEO_OV7670 + tristate "OmniVision OV7670 sensor support" + depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This is a Video4Linux2 sensor-level driver for the OmniVision + OV7670 VGA camera. It currently only works with the M88ALP01 + controller. + +config VIDEO_VS6624 + tristate "ST VS6624 sensor support" + depends on VIDEO_V4L2 && I2C + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This is a Video4Linux2 sensor-level driver for the ST VS6624 + camera. + + To compile this driver as a module, choose M here: the + module will be called vs6624. + +config VIDEO_MT9M032 + tristate "MT9M032 camera sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select VIDEO_APTINA_PLL + ---help--- + This driver supports MT9M032 camera sensors from Aptina, monochrome + models only. + +config VIDEO_MT9P031 + tristate "Aptina MT9P031 support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select VIDEO_APTINA_PLL + ---help--- + This is a Video4Linux2 sensor-level driver for the Aptina + (Micron) mt9p031 5 Mpixel camera. + +config VIDEO_MT9T001 + tristate "Aptina MT9T001 support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This is a Video4Linux2 sensor-level driver for the Aptina + (Micron) mt0t001 3 Mpixel camera. + +config VIDEO_MT9V011 + tristate "Micron mt9v011 sensor support" + depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This is a Video4Linux2 sensor-level driver for the Micron + mt0v011 1.3 Mpixel camera. It currently only works with the + em28xx driver. + +config VIDEO_MT9V032 + tristate "Micron MT9V032 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This is a Video4Linux2 sensor-level driver for the Micron + MT9V032 752x480 CMOS sensor. + +config VIDEO_TCM825X + tristate "TCM825x camera sensor support" + depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This is a driver for the Toshiba TCM825x VGA camera sensor. + It is used for example in Nokia N800. + +config VIDEO_SR030PC30 + tristate "Siliconfile SR030PC30 sensor support" + depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This driver supports SR030PC30 VGA camera from Siliconfile + +config VIDEO_NOON010PC30 + tristate "Siliconfile NOON010PC30 sensor support" + depends on I2C && VIDEO_V4L2 && EXPERIMENTAL && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This driver supports NOON010PC30 CIF camera from Siliconfile + +source "drivers/media/i2c/m5mols/Kconfig" + +config VIDEO_S5K6AA + tristate "Samsung S5K6AAFX sensor support" + depends on MEDIA_CAMERA_SUPPORT + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + ---help--- + This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M + camera sensor with an embedded SoC image signal processor. + +source "drivers/media/i2c/smiapp/Kconfig" + +comment "Flash devices" + +config VIDEO_ADP1653 + tristate "ADP1653 flash support" + depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This is a driver for the ADP1653 flash controller. It is used for + example in Nokia N900. + +config VIDEO_AS3645A + tristate "AS3645A flash driver support" + depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This is a driver for the AS3645A and LM3555 flash controllers. It has + build in control for flash, torch and indicator LEDs. + +comment "Video improvement chips" + +config VIDEO_UPD64031A + tristate "NEC Electronics uPD64031A Ghost Reduction" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the NEC Electronics uPD64031A Ghost Reduction + video chip. It is most often found in NTSC TV cards made for + Japan and is used to reduce the 'ghosting' effect that can + be present in analog TV broadcasts. + + To compile this driver as a module, choose M here: the + module will be called upd64031a. + +config VIDEO_UPD64083 + tristate "NEC Electronics uPD64083 3-Dimensional Y/C separation" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the NEC Electronics uPD64083 3-Dimensional Y/C + separation video chip. It is used to improve the quality of + the colors of a composite signal. + + To compile this driver as a module, choose M here: the + module will be called upd64083. + +comment "Miscelaneous helper chips" + +config VIDEO_THS7303 + tristate "THS7303 Video Amplifier" + depends on I2C + help + Support for TI THS7303 video amplifier + + To compile this driver as a module, choose M here: the + module will be called ths7303. + +config VIDEO_M52790 + tristate "Mitsubishi M52790 A/V switch" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Mitsubishi M52790 A/V switch. + + To compile this driver as a module, choose M here: the + module will be called m52790. + +endmenu +endif diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile new file mode 100644 index 00000000000..93e8c143959 --- /dev/null +++ b/drivers/media/i2c/Makefile @@ -0,0 +1,63 @@ +msp3400-objs := msp3400-driver.o msp3400-kthreads.o +obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o + +obj-$(CONFIG_VIDEO_SMIAPP) += smiapp/ +obj-$(CONFIG_VIDEO_CX25840) += cx25840/ +obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/ + +obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o +obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o +obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o +obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o +obj-$(CONFIG_VIDEO_TDA9840) += tda9840.o +obj-$(CONFIG_VIDEO_TEA6415C) += tea6415c.o +obj-$(CONFIG_VIDEO_TEA6420) += tea6420.o +obj-$(CONFIG_VIDEO_SAA7110) += saa7110.o +obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o +obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o +obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o +obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o +obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o +obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o +obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o +obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o +obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o +obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o +obj-$(CONFIG_VIDEO_ADV7393) += adv7393.o +obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o +obj-$(CONFIG_VIDEO_VS6624) += vs6624.o +obj-$(CONFIG_VIDEO_BT819) += bt819.o +obj-$(CONFIG_VIDEO_BT856) += bt856.o +obj-$(CONFIG_VIDEO_BT866) += bt866.o +obj-$(CONFIG_VIDEO_KS0127) += ks0127.o +obj-$(CONFIG_VIDEO_THS7303) += ths7303.o +obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o +obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o +obj-$(CONFIG_VIDEO_TVP7002) += tvp7002.o +obj-$(CONFIG_VIDEO_CS5345) += cs5345.o +obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o +obj-$(CONFIG_VIDEO_M52790) += m52790.o +obj-$(CONFIG_VIDEO_TLV320AIC23B) += tlv320aic23b.o +obj-$(CONFIG_VIDEO_WM8775) += wm8775.o +obj-$(CONFIG_VIDEO_WM8739) += wm8739.o +obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o +obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o +obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o +obj-$(CONFIG_VIDEO_OV7670) += ov7670.o +obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o +obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o +obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o +obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o +obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o +obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o +obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o +obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o +obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o +obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o +obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o +obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o +obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o +obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o +obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o +obj-$(CONFIG_VIDEO_AK881X) += ak881x.o +obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o diff --git a/drivers/media/i2c/adp1653.c b/drivers/media/i2c/adp1653.c new file mode 100644 index 00000000000..18a38b38fcb --- /dev/null +++ b/drivers/media/i2c/adp1653.c @@ -0,0 +1,487 @@ +/* + * drivers/media/i2c/adp1653.c + * + * Copyright (C) 2008--2011 Nokia Corporation + * + * Contact: Sakari Ailus + * + * Contributors: + * Sakari Ailus + * Tuukka Toivonen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * TODO: + * - fault interrupt handling + * - hardware strobe + * - power doesn't need to be ON if all lights are off + * + */ + +#include +#include +#include +#include +#include +#include + +#define TIMEOUT_MAX 820000 +#define TIMEOUT_STEP 54600 +#define TIMEOUT_MIN (TIMEOUT_MAX - ADP1653_REG_CONFIG_TMR_SET_MAX \ + * TIMEOUT_STEP) +#define TIMEOUT_US_TO_CODE(t) ((TIMEOUT_MAX + (TIMEOUT_STEP / 2) - (t)) \ + / TIMEOUT_STEP) +#define TIMEOUT_CODE_TO_US(c) (TIMEOUT_MAX - (c) * TIMEOUT_STEP) + +/* Write values into ADP1653 registers. */ +static int adp1653_update_hw(struct adp1653_flash *flash) +{ + struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); + u8 out_sel; + u8 config = 0; + int rval; + + out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG( + flash->indicator_intensity->val) + << ADP1653_REG_OUT_SEL_ILED_SHIFT; + + switch (flash->led_mode->val) { + case V4L2_FLASH_LED_MODE_NONE: + break; + case V4L2_FLASH_LED_MODE_FLASH: + /* Flash mode, light on with strobe, duration from timer */ + config = ADP1653_REG_CONFIG_TMR_CFG; + config |= TIMEOUT_US_TO_CODE(flash->flash_timeout->val) + << ADP1653_REG_CONFIG_TMR_SET_SHIFT; + break; + case V4L2_FLASH_LED_MODE_TORCH: + /* Torch mode, light immediately on, duration indefinite */ + out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG( + flash->torch_intensity->val) + << ADP1653_REG_OUT_SEL_HPLED_SHIFT; + break; + } + + rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel); + if (rval < 0) + return rval; + + rval = i2c_smbus_write_byte_data(client, ADP1653_REG_CONFIG, config); + if (rval < 0) + return rval; + + return 0; +} + +static int adp1653_get_fault(struct adp1653_flash *flash) +{ + struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); + int fault; + int rval; + + fault = i2c_smbus_read_byte_data(client, ADP1653_REG_FAULT); + if (IS_ERR_VALUE(fault)) + return fault; + + flash->fault |= fault; + + if (!flash->fault) + return 0; + + /* Clear faults. */ + rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0); + if (IS_ERR_VALUE(rval)) + return rval; + + flash->led_mode->val = V4L2_FLASH_LED_MODE_NONE; + + rval = adp1653_update_hw(flash); + if (IS_ERR_VALUE(rval)) + return rval; + + return flash->fault; +} + +static int adp1653_strobe(struct adp1653_flash *flash, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); + u8 out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG( + flash->indicator_intensity->val) + << ADP1653_REG_OUT_SEL_ILED_SHIFT; + int rval; + + if (flash->led_mode->val != V4L2_FLASH_LED_MODE_FLASH) + return -EBUSY; + + if (!enable) + return i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, + out_sel); + + out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG( + flash->flash_intensity->val) + << ADP1653_REG_OUT_SEL_HPLED_SHIFT; + rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel); + if (rval) + return rval; + + /* Software strobe using i2c */ + rval = i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE, + ADP1653_REG_SW_STROBE_SW_STROBE); + if (rval) + return rval; + return i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE, 0); +} + +/* -------------------------------------------------------------------------- + * V4L2 controls + */ + +static int adp1653_get_ctrl(struct v4l2_ctrl *ctrl) +{ + struct adp1653_flash *flash = + container_of(ctrl->handler, struct adp1653_flash, ctrls); + int rval; + + rval = adp1653_get_fault(flash); + if (IS_ERR_VALUE(rval)) + return rval; + + ctrl->cur.val = 0; + + if (flash->fault & ADP1653_REG_FAULT_FLT_SCP) + ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT; + if (flash->fault & ADP1653_REG_FAULT_FLT_OT) + ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE; + if (flash->fault & ADP1653_REG_FAULT_FLT_TMR) + ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT; + if (flash->fault & ADP1653_REG_FAULT_FLT_OV) + ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE; + + flash->fault = 0; + + return 0; +} + +static int adp1653_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct adp1653_flash *flash = + container_of(ctrl->handler, struct adp1653_flash, ctrls); + int rval; + + rval = adp1653_get_fault(flash); + if (IS_ERR_VALUE(rval)) + return rval; + if ((rval & (ADP1653_REG_FAULT_FLT_SCP | + ADP1653_REG_FAULT_FLT_OT | + ADP1653_REG_FAULT_FLT_OV)) && + (ctrl->id == V4L2_CID_FLASH_STROBE || + ctrl->id == V4L2_CID_FLASH_TORCH_INTENSITY || + ctrl->id == V4L2_CID_FLASH_LED_MODE)) + return -EBUSY; + + switch (ctrl->id) { + case V4L2_CID_FLASH_STROBE: + return adp1653_strobe(flash, 1); + case V4L2_CID_FLASH_STROBE_STOP: + return adp1653_strobe(flash, 0); + } + + return adp1653_update_hw(flash); +} + +static const struct v4l2_ctrl_ops adp1653_ctrl_ops = { + .g_volatile_ctrl = adp1653_get_ctrl, + .s_ctrl = adp1653_set_ctrl, +}; + +static int adp1653_init_controls(struct adp1653_flash *flash) +{ + struct v4l2_ctrl *fault; + + v4l2_ctrl_handler_init(&flash->ctrls, 9); + + flash->led_mode = + v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops, + V4L2_CID_FLASH_LED_MODE, + V4L2_FLASH_LED_MODE_TORCH, ~0x7, 0); + v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops, + V4L2_CID_FLASH_STROBE_SOURCE, + V4L2_FLASH_STROBE_SOURCE_SOFTWARE, ~0x1, 0); + v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, + V4L2_CID_FLASH_STROBE, 0, 0, 0, 0); + v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, + V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0); + flash->flash_timeout = + v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, + V4L2_CID_FLASH_TIMEOUT, TIMEOUT_MIN, + flash->platform_data->max_flash_timeout, + TIMEOUT_STEP, + flash->platform_data->max_flash_timeout); + flash->flash_intensity = + v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, + V4L2_CID_FLASH_INTENSITY, + ADP1653_FLASH_INTENSITY_MIN, + flash->platform_data->max_flash_intensity, + 1, flash->platform_data->max_flash_intensity); + flash->torch_intensity = + v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, + V4L2_CID_FLASH_TORCH_INTENSITY, + ADP1653_TORCH_INTENSITY_MIN, + flash->platform_data->max_torch_intensity, + ADP1653_FLASH_INTENSITY_STEP, + flash->platform_data->max_torch_intensity); + flash->indicator_intensity = + v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, + V4L2_CID_FLASH_INDICATOR_INTENSITY, + ADP1653_INDICATOR_INTENSITY_MIN, + flash->platform_data->max_indicator_intensity, + ADP1653_INDICATOR_INTENSITY_STEP, + ADP1653_INDICATOR_INTENSITY_MIN); + fault = v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, + V4L2_CID_FLASH_FAULT, 0, + V4L2_FLASH_FAULT_OVER_VOLTAGE + | V4L2_FLASH_FAULT_OVER_TEMPERATURE + | V4L2_FLASH_FAULT_SHORT_CIRCUIT, 0, 0); + + if (flash->ctrls.error) + return flash->ctrls.error; + + fault->flags |= V4L2_CTRL_FLAG_VOLATILE; + + flash->subdev.ctrl_handler = &flash->ctrls; + return 0; +} + +/* -------------------------------------------------------------------------- + * V4L2 subdev operations + */ + +static int +adp1653_init_device(struct adp1653_flash *flash) +{ + struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); + int rval; + + /* Clear FAULT register by writing zero to OUT_SEL */ + rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0); + if (rval < 0) { + dev_err(&client->dev, "failed writing fault register\n"); + return -EIO; + } + + mutex_lock(flash->ctrls.lock); + /* Reset faults before reading new ones. */ + flash->fault = 0; + rval = adp1653_get_fault(flash); + mutex_unlock(flash->ctrls.lock); + if (rval > 0) { + dev_err(&client->dev, "faults detected: 0x%1.1x\n", rval); + return -EIO; + } + + mutex_lock(flash->ctrls.lock); + rval = adp1653_update_hw(flash); + mutex_unlock(flash->ctrls.lock); + if (rval) { + dev_err(&client->dev, + "adp1653_update_hw failed at %s\n", __func__); + return -EIO; + } + + return 0; +} + +static int +__adp1653_set_power(struct adp1653_flash *flash, int on) +{ + int ret; + + ret = flash->platform_data->power(&flash->subdev, on); + if (ret < 0) + return ret; + + if (!on) + return 0; + + ret = adp1653_init_device(flash); + if (ret < 0) + flash->platform_data->power(&flash->subdev, 0); + + return ret; +} + +static int +adp1653_set_power(struct v4l2_subdev *subdev, int on) +{ + struct adp1653_flash *flash = to_adp1653_flash(subdev); + int ret = 0; + + mutex_lock(&flash->power_lock); + + /* If the power count is modified from 0 to != 0 or from != 0 to 0, + * update the power state. + */ + if (flash->power_count == !on) { + ret = __adp1653_set_power(flash, !!on); + if (ret < 0) + goto done; + } + + /* Update the power count. */ + flash->power_count += on ? 1 : -1; + WARN_ON(flash->power_count < 0); + +done: + mutex_unlock(&flash->power_lock); + return ret; +} + +static int adp1653_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + return adp1653_set_power(sd, 1); +} + +static int adp1653_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + return adp1653_set_power(sd, 0); +} + +static const struct v4l2_subdev_core_ops adp1653_core_ops = { + .s_power = adp1653_set_power, +}; + +static const struct v4l2_subdev_ops adp1653_ops = { + .core = &adp1653_core_ops, +}; + +static const struct v4l2_subdev_internal_ops adp1653_internal_ops = { + .open = adp1653_open, + .close = adp1653_close, +}; + +/* -------------------------------------------------------------------------- + * I2C driver + */ +#ifdef CONFIG_PM + +static int adp1653_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct adp1653_flash *flash = to_adp1653_flash(subdev); + + if (!flash->power_count) + return 0; + + return __adp1653_set_power(flash, 0); +} + +static int adp1653_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct adp1653_flash *flash = to_adp1653_flash(subdev); + + if (!flash->power_count) + return 0; + + return __adp1653_set_power(flash, 1); +} + +#else + +#define adp1653_suspend NULL +#define adp1653_resume NULL + +#endif /* CONFIG_PM */ + +static int adp1653_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + struct adp1653_flash *flash; + int ret; + + /* we couldn't work without platform data */ + if (client->dev.platform_data == NULL) + return -ENODEV; + + flash = kzalloc(sizeof(*flash), GFP_KERNEL); + if (flash == NULL) + return -ENOMEM; + + flash->platform_data = client->dev.platform_data; + + mutex_init(&flash->power_lock); + + v4l2_i2c_subdev_init(&flash->subdev, client, &adp1653_ops); + flash->subdev.internal_ops = &adp1653_internal_ops; + flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + ret = adp1653_init_controls(flash); + if (ret) + goto free_and_quit; + + ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0); + if (ret < 0) + goto free_and_quit; + + flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; + + return 0; + +free_and_quit: + v4l2_ctrl_handler_free(&flash->ctrls); + kfree(flash); + return ret; +} + +static int __exit adp1653_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct adp1653_flash *flash = to_adp1653_flash(subdev); + + v4l2_device_unregister_subdev(&flash->subdev); + v4l2_ctrl_handler_free(&flash->ctrls); + media_entity_cleanup(&flash->subdev.entity); + kfree(flash); + return 0; +} + +static const struct i2c_device_id adp1653_id_table[] = { + { ADP1653_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adp1653_id_table); + +static struct dev_pm_ops adp1653_pm_ops = { + .suspend = adp1653_suspend, + .resume = adp1653_resume, +}; + +static struct i2c_driver adp1653_i2c_driver = { + .driver = { + .name = ADP1653_NAME, + .pm = &adp1653_pm_ops, + }, + .probe = adp1653_probe, + .remove = __exit_p(adp1653_remove), + .id_table = adp1653_id_table, +}; + +module_i2c_driver(adp1653_i2c_driver); + +MODULE_AUTHOR("Sakari Ailus "); +MODULE_DESCRIPTION("Analog Devices ADP1653 LED flash driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/adv7170.c b/drivers/media/i2c/adv7170.c new file mode 100644 index 00000000000..6bc01fb98ff --- /dev/null +++ b/drivers/media/i2c/adv7170.c @@ -0,0 +1,410 @@ +/* + * adv7170 - adv7170, adv7171 video encoder driver version 0.0.1 + * + * Copyright (C) 2002 Maxim Yevtyushkin + * + * Based on adv7176 driver by: + * + * Copyright (C) 1998 Dave Perks + * Copyright (C) 1999 Wolfgang Scherr + * Copyright (C) 2000 Serguei Miridonov + * - some corrections for Pinnacle Systems Inc. DC10plus card. + * + * Changes by Ronald Bultje + * - moved over to linux>=2.4.x i2c protocol (1/1/2003) + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("Analog Devices ADV7170 video encoder driver"); +MODULE_AUTHOR("Maxim Yevtyushkin"); +MODULE_LICENSE("GPL"); + + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +/* ----------------------------------------------------------------------- */ + +struct adv7170 { + struct v4l2_subdev sd; + unsigned char reg[128]; + + v4l2_std_id norm; + int input; +}; + +static inline struct adv7170 *to_adv7170(struct v4l2_subdev *sd) +{ + return container_of(sd, struct adv7170, sd); +} + +static char *inputs[] = { "pass_through", "play_back" }; + +static enum v4l2_mbus_pixelcode adv7170_codes[] = { + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_UYVY8_1X16, +}; + +/* ----------------------------------------------------------------------- */ + +static inline int adv7170_write(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct adv7170 *encoder = to_adv7170(sd); + + encoder->reg[reg] = value; + return i2c_smbus_write_byte_data(client, reg, value); +} + +static inline int adv7170_read(struct v4l2_subdev *sd, u8 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_read_byte_data(client, reg); +} + +static int adv7170_write_block(struct v4l2_subdev *sd, + const u8 *data, unsigned int len) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct adv7170 *encoder = to_adv7170(sd); + int ret = -1; + u8 reg; + + /* the adv7170 has an autoincrement function, use it if + * the adapter understands raw I2C */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + /* do raw I2C, not smbus compatible */ + u8 block_data[32]; + int block_len; + + while (len >= 2) { + block_len = 0; + block_data[block_len++] = reg = data[0]; + do { + block_data[block_len++] = + encoder->reg[reg++] = data[1]; + len -= 2; + data += 2; + } while (len >= 2 && data[0] == reg && block_len < 32); + ret = i2c_master_send(client, block_data, block_len); + if (ret < 0) + break; + } + } else { + /* do some slow I2C emulation kind of thing */ + while (len >= 2) { + reg = *data++; + ret = adv7170_write(sd, reg, *data++); + if (ret < 0) + break; + len -= 2; + } + } + return ret; +} + +/* ----------------------------------------------------------------------- */ + +#define TR0MODE 0x4c +#define TR0RST 0x80 + +#define TR1CAPT 0x00 +#define TR1PLAY 0x00 + +static const unsigned char init_NTSC[] = { + 0x00, 0x10, /* MR0 */ + 0x01, 0x20, /* MR1 */ + 0x02, 0x0e, /* MR2 RTC control: bits 2 and 1 */ + 0x03, 0x80, /* MR3 */ + 0x04, 0x30, /* MR4 */ + 0x05, 0x00, /* Reserved */ + 0x06, 0x00, /* Reserved */ + 0x07, TR0MODE, /* TM0 */ + 0x08, TR1CAPT, /* TM1 */ + 0x09, 0x16, /* Fsc0 */ + 0x0a, 0x7c, /* Fsc1 */ + 0x0b, 0xf0, /* Fsc2 */ + 0x0c, 0x21, /* Fsc3 */ + 0x0d, 0x00, /* Subcarrier Phase */ + 0x0e, 0x00, /* Closed Capt. Ext 0 */ + 0x0f, 0x00, /* Closed Capt. Ext 1 */ + 0x10, 0x00, /* Closed Capt. 0 */ + 0x11, 0x00, /* Closed Capt. 1 */ + 0x12, 0x00, /* Pedestal Ctl 0 */ + 0x13, 0x00, /* Pedestal Ctl 1 */ + 0x14, 0x00, /* Pedestal Ctl 2 */ + 0x15, 0x00, /* Pedestal Ctl 3 */ + 0x16, 0x00, /* CGMS_WSS_0 */ + 0x17, 0x00, /* CGMS_WSS_1 */ + 0x18, 0x00, /* CGMS_WSS_2 */ + 0x19, 0x00, /* Teletext Ctl */ +}; + +static const unsigned char init_PAL[] = { + 0x00, 0x71, /* MR0 */ + 0x01, 0x20, /* MR1 */ + 0x02, 0x0e, /* MR2 RTC control: bits 2 and 1 */ + 0x03, 0x80, /* MR3 */ + 0x04, 0x30, /* MR4 */ + 0x05, 0x00, /* Reserved */ + 0x06, 0x00, /* Reserved */ + 0x07, TR0MODE, /* TM0 */ + 0x08, TR1CAPT, /* TM1 */ + 0x09, 0xcb, /* Fsc0 */ + 0x0a, 0x8a, /* Fsc1 */ + 0x0b, 0x09, /* Fsc2 */ + 0x0c, 0x2a, /* Fsc3 */ + 0x0d, 0x00, /* Subcarrier Phase */ + 0x0e, 0x00, /* Closed Capt. Ext 0 */ + 0x0f, 0x00, /* Closed Capt. Ext 1 */ + 0x10, 0x00, /* Closed Capt. 0 */ + 0x11, 0x00, /* Closed Capt. 1 */ + 0x12, 0x00, /* Pedestal Ctl 0 */ + 0x13, 0x00, /* Pedestal Ctl 1 */ + 0x14, 0x00, /* Pedestal Ctl 2 */ + 0x15, 0x00, /* Pedestal Ctl 3 */ + 0x16, 0x00, /* CGMS_WSS_0 */ + 0x17, 0x00, /* CGMS_WSS_1 */ + 0x18, 0x00, /* CGMS_WSS_2 */ + 0x19, 0x00, /* Teletext Ctl */ +}; + + +static int adv7170_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7170 *encoder = to_adv7170(sd); + + v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); + + if (std & V4L2_STD_NTSC) { + adv7170_write_block(sd, init_NTSC, sizeof(init_NTSC)); + if (encoder->input == 0) + adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */ + adv7170_write(sd, 0x07, TR0MODE | TR0RST); + adv7170_write(sd, 0x07, TR0MODE); + } else if (std & V4L2_STD_PAL) { + adv7170_write_block(sd, init_PAL, sizeof(init_PAL)); + if (encoder->input == 0) + adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */ + adv7170_write(sd, 0x07, TR0MODE | TR0RST); + adv7170_write(sd, 0x07, TR0MODE); + } else { + v4l2_dbg(1, debug, sd, "illegal norm: %llx\n", + (unsigned long long)std); + return -EINVAL; + } + v4l2_dbg(1, debug, sd, "switched to %llx\n", (unsigned long long)std); + encoder->norm = std; + return 0; +} + +static int adv7170_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct adv7170 *encoder = to_adv7170(sd); + + /* RJ: input = 0: input is from decoder + input = 1: input is from ZR36060 + input = 2: color bar */ + + v4l2_dbg(1, debug, sd, "set input from %s\n", + input == 0 ? "decoder" : "ZR36060"); + + switch (input) { + case 0: + adv7170_write(sd, 0x01, 0x20); + adv7170_write(sd, 0x08, TR1CAPT); /* TR1 */ + adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */ + adv7170_write(sd, 0x07, TR0MODE | TR0RST); + adv7170_write(sd, 0x07, TR0MODE); + /* udelay(10); */ + break; + + case 1: + adv7170_write(sd, 0x01, 0x00); + adv7170_write(sd, 0x08, TR1PLAY); /* TR1 */ + adv7170_write(sd, 0x02, 0x08); + adv7170_write(sd, 0x07, TR0MODE | TR0RST); + adv7170_write(sd, 0x07, TR0MODE); + /* udelay(10); */ + break; + + default: + v4l2_dbg(1, debug, sd, "illegal input: %d\n", input); + return -EINVAL; + } + v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[input]); + encoder->input = input; + return 0; +} + +static int adv7170_enum_fmt(struct v4l2_subdev *sd, unsigned int index, + enum v4l2_mbus_pixelcode *code) +{ + if (index >= ARRAY_SIZE(adv7170_codes)) + return -EINVAL; + + *code = adv7170_codes[index]; + return 0; +} + +static int adv7170_g_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + u8 val = adv7170_read(sd, 0x7); + + if ((val & 0x40) == (1 << 6)) + mf->code = V4L2_MBUS_FMT_UYVY8_1X16; + else + mf->code = V4L2_MBUS_FMT_UYVY8_2X8; + + mf->colorspace = V4L2_COLORSPACE_SMPTE170M; + mf->width = 0; + mf->height = 0; + mf->field = V4L2_FIELD_ANY; + + return 0; +} + +static int adv7170_s_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + u8 val = adv7170_read(sd, 0x7); + int ret; + + switch (mf->code) { + case V4L2_MBUS_FMT_UYVY8_2X8: + val &= ~0x40; + break; + + case V4L2_MBUS_FMT_UYVY8_1X16: + val |= 0x40; + break; + + default: + v4l2_dbg(1, debug, sd, + "illegal v4l2_mbus_framefmt code: %d\n", mf->code); + return -EINVAL; + } + + ret = adv7170_write(sd, 0x7, val); + + return ret; +} + +static int adv7170_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7170, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops adv7170_core_ops = { + .g_chip_ident = adv7170_g_chip_ident, +}; + +static const struct v4l2_subdev_video_ops adv7170_video_ops = { + .s_std_output = adv7170_s_std_output, + .s_routing = adv7170_s_routing, + .s_mbus_fmt = adv7170_s_fmt, + .g_mbus_fmt = adv7170_g_fmt, + .enum_mbus_fmt = adv7170_enum_fmt, +}; + +static const struct v4l2_subdev_ops adv7170_ops = { + .core = &adv7170_core_ops, + .video = &adv7170_video_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static int adv7170_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adv7170 *encoder; + struct v4l2_subdev *sd; + int i; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + encoder = kzalloc(sizeof(struct adv7170), GFP_KERNEL); + if (encoder == NULL) + return -ENOMEM; + sd = &encoder->sd; + v4l2_i2c_subdev_init(sd, client, &adv7170_ops); + encoder->norm = V4L2_STD_NTSC; + encoder->input = 0; + + i = adv7170_write_block(sd, init_NTSC, sizeof(init_NTSC)); + if (i >= 0) { + i = adv7170_write(sd, 0x07, TR0MODE | TR0RST); + i = adv7170_write(sd, 0x07, TR0MODE); + i = adv7170_read(sd, 0x12); + v4l2_dbg(1, debug, sd, "revision %d\n", i & 1); + } + if (i < 0) + v4l2_dbg(1, debug, sd, "init error 0x%x\n", i); + return 0; +} + +static int adv7170_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_adv7170(sd)); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id adv7170_id[] = { + { "adv7170", 0 }, + { "adv7171", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adv7170_id); + +static struct i2c_driver adv7170_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "adv7170", + }, + .probe = adv7170_probe, + .remove = adv7170_remove, + .id_table = adv7170_id, +}; + +module_i2c_driver(adv7170_driver); diff --git a/drivers/media/i2c/adv7175.c b/drivers/media/i2c/adv7175.c new file mode 100644 index 00000000000..c7640fab573 --- /dev/null +++ b/drivers/media/i2c/adv7175.c @@ -0,0 +1,460 @@ +/* + * adv7175 - adv7175a video encoder driver version 0.0.3 + * + * Copyright (C) 1998 Dave Perks + * Copyright (C) 1999 Wolfgang Scherr + * Copyright (C) 2000 Serguei Miridonov + * - some corrections for Pinnacle Systems Inc. DC10plus card. + * + * Changes by Ronald Bultje + * - moved over to linux>=2.4.x i2c protocol (9/9/2002) + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("Analog Devices ADV7175 video encoder driver"); +MODULE_AUTHOR("Dave Perks"); +MODULE_LICENSE("GPL"); + +#define I2C_ADV7175 0xd4 +#define I2C_ADV7176 0x54 + + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +/* ----------------------------------------------------------------------- */ + +struct adv7175 { + struct v4l2_subdev sd; + v4l2_std_id norm; + int input; +}; + +static inline struct adv7175 *to_adv7175(struct v4l2_subdev *sd) +{ + return container_of(sd, struct adv7175, sd); +} + +static char *inputs[] = { "pass_through", "play_back", "color_bar" }; + +static enum v4l2_mbus_pixelcode adv7175_codes[] = { + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_UYVY8_1X16, +}; + +/* ----------------------------------------------------------------------- */ + +static inline int adv7175_write(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_write_byte_data(client, reg, value); +} + +static inline int adv7175_read(struct v4l2_subdev *sd, u8 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_read_byte_data(client, reg); +} + +static int adv7175_write_block(struct v4l2_subdev *sd, + const u8 *data, unsigned int len) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = -1; + u8 reg; + + /* the adv7175 has an autoincrement function, use it if + * the adapter understands raw I2C */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + /* do raw I2C, not smbus compatible */ + u8 block_data[32]; + int block_len; + + while (len >= 2) { + block_len = 0; + block_data[block_len++] = reg = data[0]; + do { + block_data[block_len++] = data[1]; + reg++; + len -= 2; + data += 2; + } while (len >= 2 && data[0] == reg && block_len < 32); + ret = i2c_master_send(client, block_data, block_len); + if (ret < 0) + break; + } + } else { + /* do some slow I2C emulation kind of thing */ + while (len >= 2) { + reg = *data++; + ret = adv7175_write(sd, reg, *data++); + if (ret < 0) + break; + len -= 2; + } + } + + return ret; +} + +static void set_subcarrier_freq(struct v4l2_subdev *sd, int pass_through) +{ + /* for some reason pass_through NTSC needs + * a different sub-carrier freq to remain stable. */ + if (pass_through) + adv7175_write(sd, 0x02, 0x00); + else + adv7175_write(sd, 0x02, 0x55); + + adv7175_write(sd, 0x03, 0x55); + adv7175_write(sd, 0x04, 0x55); + adv7175_write(sd, 0x05, 0x25); +} + +/* ----------------------------------------------------------------------- */ +/* Output filter: S-Video Composite */ + +#define MR050 0x11 /* 0x09 */ +#define MR060 0x14 /* 0x0c */ + +/* ----------------------------------------------------------------------- */ + +#define TR0MODE 0x46 +#define TR0RST 0x80 + +#define TR1CAPT 0x80 +#define TR1PLAY 0x00 + +static const unsigned char init_common[] = { + + 0x00, MR050, /* MR0, PAL enabled */ + 0x01, 0x00, /* MR1 */ + 0x02, 0x0c, /* subc. freq. */ + 0x03, 0x8c, /* subc. freq. */ + 0x04, 0x79, /* subc. freq. */ + 0x05, 0x26, /* subc. freq. */ + 0x06, 0x40, /* subc. phase */ + + 0x07, TR0MODE, /* TR0, 16bit */ + 0x08, 0x21, /* */ + 0x09, 0x00, /* */ + 0x0a, 0x00, /* */ + 0x0b, 0x00, /* */ + 0x0c, TR1CAPT, /* TR1 */ + 0x0d, 0x4f, /* MR2 */ + 0x0e, 0x00, /* */ + 0x0f, 0x00, /* */ + 0x10, 0x00, /* */ + 0x11, 0x00, /* */ +}; + +static const unsigned char init_pal[] = { + 0x00, MR050, /* MR0, PAL enabled */ + 0x01, 0x00, /* MR1 */ + 0x02, 0x0c, /* subc. freq. */ + 0x03, 0x8c, /* subc. freq. */ + 0x04, 0x79, /* subc. freq. */ + 0x05, 0x26, /* subc. freq. */ + 0x06, 0x40, /* subc. phase */ +}; + +static const unsigned char init_ntsc[] = { + 0x00, MR060, /* MR0, NTSC enabled */ + 0x01, 0x00, /* MR1 */ + 0x02, 0x55, /* subc. freq. */ + 0x03, 0x55, /* subc. freq. */ + 0x04, 0x55, /* subc. freq. */ + 0x05, 0x25, /* subc. freq. */ + 0x06, 0x1a, /* subc. phase */ +}; + +static int adv7175_init(struct v4l2_subdev *sd, u32 val) +{ + /* This is just for testing!!! */ + adv7175_write_block(sd, init_common, sizeof(init_common)); + adv7175_write(sd, 0x07, TR0MODE | TR0RST); + adv7175_write(sd, 0x07, TR0MODE); + return 0; +} + +static int adv7175_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7175 *encoder = to_adv7175(sd); + + if (std & V4L2_STD_NTSC) { + adv7175_write_block(sd, init_ntsc, sizeof(init_ntsc)); + if (encoder->input == 0) + adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */ + adv7175_write(sd, 0x07, TR0MODE | TR0RST); + adv7175_write(sd, 0x07, TR0MODE); + } else if (std & V4L2_STD_PAL) { + adv7175_write_block(sd, init_pal, sizeof(init_pal)); + if (encoder->input == 0) + adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */ + adv7175_write(sd, 0x07, TR0MODE | TR0RST); + adv7175_write(sd, 0x07, TR0MODE); + } else if (std & V4L2_STD_SECAM) { + /* This is an attempt to convert + * SECAM->PAL (typically it does not work + * due to genlock: when decoder is in SECAM + * and encoder in in PAL the subcarrier can + * not be syncronized with horizontal + * quency) */ + adv7175_write_block(sd, init_pal, sizeof(init_pal)); + if (encoder->input == 0) + adv7175_write(sd, 0x0d, 0x49); /* Disable genlock */ + adv7175_write(sd, 0x07, TR0MODE | TR0RST); + adv7175_write(sd, 0x07, TR0MODE); + } else { + v4l2_dbg(1, debug, sd, "illegal norm: %llx\n", + (unsigned long long)std); + return -EINVAL; + } + v4l2_dbg(1, debug, sd, "switched to %llx\n", (unsigned long long)std); + encoder->norm = std; + return 0; +} + +static int adv7175_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct adv7175 *encoder = to_adv7175(sd); + + /* RJ: input = 0: input is from decoder + input = 1: input is from ZR36060 + input = 2: color bar */ + + switch (input) { + case 0: + adv7175_write(sd, 0x01, 0x00); + + if (encoder->norm & V4L2_STD_NTSC) + set_subcarrier_freq(sd, 1); + + adv7175_write(sd, 0x0c, TR1CAPT); /* TR1 */ + if (encoder->norm & V4L2_STD_SECAM) + adv7175_write(sd, 0x0d, 0x49); /* Disable genlock */ + else + adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */ + adv7175_write(sd, 0x07, TR0MODE | TR0RST); + adv7175_write(sd, 0x07, TR0MODE); + /*udelay(10);*/ + break; + + case 1: + adv7175_write(sd, 0x01, 0x00); + + if (encoder->norm & V4L2_STD_NTSC) + set_subcarrier_freq(sd, 0); + + adv7175_write(sd, 0x0c, TR1PLAY); /* TR1 */ + adv7175_write(sd, 0x0d, 0x49); + adv7175_write(sd, 0x07, TR0MODE | TR0RST); + adv7175_write(sd, 0x07, TR0MODE); + /* udelay(10); */ + break; + + case 2: + adv7175_write(sd, 0x01, 0x80); + + if (encoder->norm & V4L2_STD_NTSC) + set_subcarrier_freq(sd, 0); + + adv7175_write(sd, 0x0d, 0x49); + adv7175_write(sd, 0x07, TR0MODE | TR0RST); + adv7175_write(sd, 0x07, TR0MODE); + /* udelay(10); */ + break; + + default: + v4l2_dbg(1, debug, sd, "illegal input: %d\n", input); + return -EINVAL; + } + v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[input]); + encoder->input = input; + return 0; +} + +static int adv7175_enum_fmt(struct v4l2_subdev *sd, unsigned int index, + enum v4l2_mbus_pixelcode *code) +{ + if (index >= ARRAY_SIZE(adv7175_codes)) + return -EINVAL; + + *code = adv7175_codes[index]; + return 0; +} + +static int adv7175_g_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + u8 val = adv7175_read(sd, 0x7); + + if ((val & 0x40) == (1 << 6)) + mf->code = V4L2_MBUS_FMT_UYVY8_1X16; + else + mf->code = V4L2_MBUS_FMT_UYVY8_2X8; + + mf->colorspace = V4L2_COLORSPACE_SMPTE170M; + mf->width = 0; + mf->height = 0; + mf->field = V4L2_FIELD_ANY; + + return 0; +} + +static int adv7175_s_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + u8 val = adv7175_read(sd, 0x7); + int ret; + + switch (mf->code) { + case V4L2_MBUS_FMT_UYVY8_2X8: + val &= ~0x40; + break; + + case V4L2_MBUS_FMT_UYVY8_1X16: + val |= 0x40; + break; + + default: + v4l2_dbg(1, debug, sd, + "illegal v4l2_mbus_framefmt code: %d\n", mf->code); + return -EINVAL; + } + + ret = adv7175_write(sd, 0x7, val); + + return ret; +} + +static int adv7175_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7175, 0); +} + +static int adv7175_s_power(struct v4l2_subdev *sd, int on) +{ + if (on) + adv7175_write(sd, 0x01, 0x00); + else + adv7175_write(sd, 0x01, 0x78); + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops adv7175_core_ops = { + .g_chip_ident = adv7175_g_chip_ident, + .init = adv7175_init, + .s_power = adv7175_s_power, +}; + +static const struct v4l2_subdev_video_ops adv7175_video_ops = { + .s_std_output = adv7175_s_std_output, + .s_routing = adv7175_s_routing, + .s_mbus_fmt = adv7175_s_fmt, + .g_mbus_fmt = adv7175_g_fmt, + .enum_mbus_fmt = adv7175_enum_fmt, +}; + +static const struct v4l2_subdev_ops adv7175_ops = { + .core = &adv7175_core_ops, + .video = &adv7175_video_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static int adv7175_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int i; + struct adv7175 *encoder; + struct v4l2_subdev *sd; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + encoder = kzalloc(sizeof(struct adv7175), GFP_KERNEL); + if (encoder == NULL) + return -ENOMEM; + sd = &encoder->sd; + v4l2_i2c_subdev_init(sd, client, &adv7175_ops); + encoder->norm = V4L2_STD_NTSC; + encoder->input = 0; + + i = adv7175_write_block(sd, init_common, sizeof(init_common)); + if (i >= 0) { + i = adv7175_write(sd, 0x07, TR0MODE | TR0RST); + i = adv7175_write(sd, 0x07, TR0MODE); + i = adv7175_read(sd, 0x12); + v4l2_dbg(1, debug, sd, "revision %d\n", i & 1); + } + if (i < 0) + v4l2_dbg(1, debug, sd, "init error 0x%x\n", i); + return 0; +} + +static int adv7175_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_adv7175(sd)); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id adv7175_id[] = { + { "adv7175", 0 }, + { "adv7176", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adv7175_id); + +static struct i2c_driver adv7175_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "adv7175", + }, + .probe = adv7175_probe, + .remove = adv7175_remove, + .id_table = adv7175_id, +}; + +module_i2c_driver(adv7175_driver); diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c new file mode 100644 index 00000000000..45ecf8db1ea --- /dev/null +++ b/drivers/media/i2c/adv7180.c @@ -0,0 +1,667 @@ +/* + * adv7180.c Analog Devices ADV7180 video decoder driver + * Copyright (c) 2009 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ADV7180_INPUT_CONTROL_REG 0x00 +#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM 0x00 +#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM_PED 0x10 +#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_J_SECAM 0x20 +#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_M_SECAM 0x30 +#define ADV7180_INPUT_CONTROL_NTSC_J 0x40 +#define ADV7180_INPUT_CONTROL_NTSC_M 0x50 +#define ADV7180_INPUT_CONTROL_PAL60 0x60 +#define ADV7180_INPUT_CONTROL_NTSC_443 0x70 +#define ADV7180_INPUT_CONTROL_PAL_BG 0x80 +#define ADV7180_INPUT_CONTROL_PAL_N 0x90 +#define ADV7180_INPUT_CONTROL_PAL_M 0xa0 +#define ADV7180_INPUT_CONTROL_PAL_M_PED 0xb0 +#define ADV7180_INPUT_CONTROL_PAL_COMB_N 0xc0 +#define ADV7180_INPUT_CONTROL_PAL_COMB_N_PED 0xd0 +#define ADV7180_INPUT_CONTROL_PAL_SECAM 0xe0 +#define ADV7180_INPUT_CONTROL_PAL_SECAM_PED 0xf0 +#define ADV7180_INPUT_CONTROL_INSEL_MASK 0x0f + +#define ADV7180_EXTENDED_OUTPUT_CONTROL_REG 0x04 +#define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS 0xC5 + +#define ADV7180_AUTODETECT_ENABLE_REG 0x07 +#define ADV7180_AUTODETECT_DEFAULT 0x7f +/* Contrast */ +#define ADV7180_CON_REG 0x08 /*Unsigned */ +#define ADV7180_CON_MIN 0 +#define ADV7180_CON_DEF 128 +#define ADV7180_CON_MAX 255 +/* Brightness*/ +#define ADV7180_BRI_REG 0x0a /*Signed */ +#define ADV7180_BRI_MIN -128 +#define ADV7180_BRI_DEF 0 +#define ADV7180_BRI_MAX 127 +/* Hue */ +#define ADV7180_HUE_REG 0x0b /*Signed, inverted */ +#define ADV7180_HUE_MIN -127 +#define ADV7180_HUE_DEF 0 +#define ADV7180_HUE_MAX 128 + +#define ADV7180_ADI_CTRL_REG 0x0e +#define ADV7180_ADI_CTRL_IRQ_SPACE 0x20 + +#define ADV7180_PWR_MAN_REG 0x0f +#define ADV7180_PWR_MAN_ON 0x04 +#define ADV7180_PWR_MAN_OFF 0x24 +#define ADV7180_PWR_MAN_RES 0x80 + +#define ADV7180_STATUS1_REG 0x10 +#define ADV7180_STATUS1_IN_LOCK 0x01 +#define ADV7180_STATUS1_AUTOD_MASK 0x70 +#define ADV7180_STATUS1_AUTOD_NTSM_M_J 0x00 +#define ADV7180_STATUS1_AUTOD_NTSC_4_43 0x10 +#define ADV7180_STATUS1_AUTOD_PAL_M 0x20 +#define ADV7180_STATUS1_AUTOD_PAL_60 0x30 +#define ADV7180_STATUS1_AUTOD_PAL_B_G 0x40 +#define ADV7180_STATUS1_AUTOD_SECAM 0x50 +#define ADV7180_STATUS1_AUTOD_PAL_COMB 0x60 +#define ADV7180_STATUS1_AUTOD_SECAM_525 0x70 + +#define ADV7180_IDENT_REG 0x11 +#define ADV7180_ID_7180 0x18 + +#define ADV7180_ICONF1_ADI 0x40 +#define ADV7180_ICONF1_ACTIVE_LOW 0x01 +#define ADV7180_ICONF1_PSYNC_ONLY 0x10 +#define ADV7180_ICONF1_ACTIVE_TO_CLR 0xC0 +/* Saturation */ +#define ADV7180_SD_SAT_CB_REG 0xe3 /*Unsigned */ +#define ADV7180_SD_SAT_CR_REG 0xe4 /*Unsigned */ +#define ADV7180_SAT_MIN 0 +#define ADV7180_SAT_DEF 128 +#define ADV7180_SAT_MAX 255 + +#define ADV7180_IRQ1_LOCK 0x01 +#define ADV7180_IRQ1_UNLOCK 0x02 +#define ADV7180_ISR1_ADI 0x42 +#define ADV7180_ICR1_ADI 0x43 +#define ADV7180_IMR1_ADI 0x44 +#define ADV7180_IMR2_ADI 0x48 +#define ADV7180_IRQ3_AD_CHANGE 0x08 +#define ADV7180_ISR3_ADI 0x4A +#define ADV7180_ICR3_ADI 0x4B +#define ADV7180_IMR3_ADI 0x4C +#define ADV7180_IMR4_ADI 0x50 + +#define ADV7180_NTSC_V_BIT_END_REG 0xE6 +#define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND 0x4F + +struct adv7180_state { + struct v4l2_ctrl_handler ctrl_hdl; + struct v4l2_subdev sd; + struct work_struct work; + struct mutex mutex; /* mutual excl. when accessing chip */ + int irq; + v4l2_std_id curr_norm; + bool autodetect; + u8 input; +}; +#define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler, \ + struct adv7180_state, \ + ctrl_hdl)->sd) + +static v4l2_std_id adv7180_std_to_v4l2(u8 status1) +{ + switch (status1 & ADV7180_STATUS1_AUTOD_MASK) { + case ADV7180_STATUS1_AUTOD_NTSM_M_J: + return V4L2_STD_NTSC; + case ADV7180_STATUS1_AUTOD_NTSC_4_43: + return V4L2_STD_NTSC_443; + case ADV7180_STATUS1_AUTOD_PAL_M: + return V4L2_STD_PAL_M; + case ADV7180_STATUS1_AUTOD_PAL_60: + return V4L2_STD_PAL_60; + case ADV7180_STATUS1_AUTOD_PAL_B_G: + return V4L2_STD_PAL; + case ADV7180_STATUS1_AUTOD_SECAM: + return V4L2_STD_SECAM; + case ADV7180_STATUS1_AUTOD_PAL_COMB: + return V4L2_STD_PAL_Nc | V4L2_STD_PAL_N; + case ADV7180_STATUS1_AUTOD_SECAM_525: + return V4L2_STD_SECAM; + default: + return V4L2_STD_UNKNOWN; + } +} + +static int v4l2_std_to_adv7180(v4l2_std_id std) +{ + if (std == V4L2_STD_PAL_60) + return ADV7180_INPUT_CONTROL_PAL60; + if (std == V4L2_STD_NTSC_443) + return ADV7180_INPUT_CONTROL_NTSC_443; + if (std == V4L2_STD_PAL_N) + return ADV7180_INPUT_CONTROL_PAL_N; + if (std == V4L2_STD_PAL_M) + return ADV7180_INPUT_CONTROL_PAL_M; + if (std == V4L2_STD_PAL_Nc) + return ADV7180_INPUT_CONTROL_PAL_COMB_N; + + if (std & V4L2_STD_PAL) + return ADV7180_INPUT_CONTROL_PAL_BG; + if (std & V4L2_STD_NTSC) + return ADV7180_INPUT_CONTROL_NTSC_M; + if (std & V4L2_STD_SECAM) + return ADV7180_INPUT_CONTROL_PAL_SECAM; + + return -EINVAL; +} + +static u32 adv7180_status_to_v4l2(u8 status1) +{ + if (!(status1 & ADV7180_STATUS1_IN_LOCK)) + return V4L2_IN_ST_NO_SIGNAL; + + return 0; +} + +static int __adv7180_status(struct i2c_client *client, u32 *status, + v4l2_std_id *std) +{ + int status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG); + + if (status1 < 0) + return status1; + + if (status) + *status = adv7180_status_to_v4l2(status1); + if (std) + *std = adv7180_std_to_v4l2(status1); + + return 0; +} + +static inline struct adv7180_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct adv7180_state, sd); +} + +static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + struct adv7180_state *state = to_state(sd); + int err = mutex_lock_interruptible(&state->mutex); + if (err) + return err; + + /* when we are interrupt driven we know the state */ + if (!state->autodetect || state->irq > 0) + *std = state->curr_norm; + else + err = __adv7180_status(v4l2_get_subdevdata(sd), NULL, std); + + mutex_unlock(&state->mutex); + return err; +} + +static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input, + u32 output, u32 config) +{ + struct adv7180_state *state = to_state(sd); + int ret = mutex_lock_interruptible(&state->mutex); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (ret) + return ret; + + /* We cannot discriminate between LQFP and 40-pin LFCSP, so accept + * all inputs and let the card driver take care of validation + */ + if ((input & ADV7180_INPUT_CONTROL_INSEL_MASK) != input) + goto out; + + ret = i2c_smbus_read_byte_data(client, ADV7180_INPUT_CONTROL_REG); + + if (ret < 0) + goto out; + + ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK; + ret = i2c_smbus_write_byte_data(client, + ADV7180_INPUT_CONTROL_REG, ret | input); + state->input = input; +out: + mutex_unlock(&state->mutex); + return ret; +} + +static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + struct adv7180_state *state = to_state(sd); + int ret = mutex_lock_interruptible(&state->mutex); + if (ret) + return ret; + + ret = __adv7180_status(v4l2_get_subdevdata(sd), status, NULL); + mutex_unlock(&state->mutex); + return ret; +} + +static int adv7180_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7180, 0); +} + +static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7180_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = mutex_lock_interruptible(&state->mutex); + if (ret) + return ret; + + /* all standards -> autodetect */ + if (std == V4L2_STD_ALL) { + ret = + i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, + ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM + | state->input); + if (ret < 0) + goto out; + + __adv7180_status(client, NULL, &state->curr_norm); + state->autodetect = true; + } else { + ret = v4l2_std_to_adv7180(std); + if (ret < 0) + goto out; + + ret = i2c_smbus_write_byte_data(client, + ADV7180_INPUT_CONTROL_REG, + ret | state->input); + if (ret < 0) + goto out; + + state->curr_norm = std; + state->autodetect = false; + } + ret = 0; +out: + mutex_unlock(&state->mutex); + return ret; +} + +static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_adv7180_sd(ctrl); + struct adv7180_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = mutex_lock_interruptible(&state->mutex); + int val; + + if (ret) + return ret; + val = ctrl->val; + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ret = i2c_smbus_write_byte_data(client, ADV7180_BRI_REG, val); + break; + case V4L2_CID_HUE: + /*Hue is inverted according to HSL chart */ + ret = i2c_smbus_write_byte_data(client, ADV7180_HUE_REG, -val); + break; + case V4L2_CID_CONTRAST: + ret = i2c_smbus_write_byte_data(client, ADV7180_CON_REG, val); + break; + case V4L2_CID_SATURATION: + /* + *This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE + *Let's not confuse the user, everybody understands saturation + */ + ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CB_REG, + val); + if (ret < 0) + break; + ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CR_REG, + val); + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&state->mutex); + return ret; +} + +static const struct v4l2_ctrl_ops adv7180_ctrl_ops = { + .s_ctrl = adv7180_s_ctrl, +}; + +static int adv7180_init_controls(struct adv7180_state *state) +{ + v4l2_ctrl_handler_init(&state->ctrl_hdl, 4); + + v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, + V4L2_CID_BRIGHTNESS, ADV7180_BRI_MIN, + ADV7180_BRI_MAX, 1, ADV7180_BRI_DEF); + v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, + V4L2_CID_CONTRAST, ADV7180_CON_MIN, + ADV7180_CON_MAX, 1, ADV7180_CON_DEF); + v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, + V4L2_CID_SATURATION, ADV7180_SAT_MIN, + ADV7180_SAT_MAX, 1, ADV7180_SAT_DEF); + v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, + V4L2_CID_HUE, ADV7180_HUE_MIN, + ADV7180_HUE_MAX, 1, ADV7180_HUE_DEF); + state->sd.ctrl_handler = &state->ctrl_hdl; + if (state->ctrl_hdl.error) { + int err = state->ctrl_hdl.error; + + v4l2_ctrl_handler_free(&state->ctrl_hdl); + return err; + } + v4l2_ctrl_handler_setup(&state->ctrl_hdl); + + return 0; +} +static void adv7180_exit_controls(struct adv7180_state *state) +{ + v4l2_ctrl_handler_free(&state->ctrl_hdl); +} + +static const struct v4l2_subdev_video_ops adv7180_video_ops = { + .querystd = adv7180_querystd, + .g_input_status = adv7180_g_input_status, + .s_routing = adv7180_s_routing, +}; + +static const struct v4l2_subdev_core_ops adv7180_core_ops = { + .g_chip_ident = adv7180_g_chip_ident, + .s_std = adv7180_s_std, + .queryctrl = v4l2_subdev_queryctrl, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, +}; + +static const struct v4l2_subdev_ops adv7180_ops = { + .core = &adv7180_core_ops, + .video = &adv7180_video_ops, +}; + +static void adv7180_work(struct work_struct *work) +{ + struct adv7180_state *state = container_of(work, struct adv7180_state, + work); + struct i2c_client *client = v4l2_get_subdevdata(&state->sd); + u8 isr3; + + mutex_lock(&state->mutex); + i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, + ADV7180_ADI_CTRL_IRQ_SPACE); + isr3 = i2c_smbus_read_byte_data(client, ADV7180_ISR3_ADI); + /* clear */ + i2c_smbus_write_byte_data(client, ADV7180_ICR3_ADI, isr3); + i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, 0); + + if (isr3 & ADV7180_IRQ3_AD_CHANGE && state->autodetect) + __adv7180_status(client, NULL, &state->curr_norm); + mutex_unlock(&state->mutex); + + enable_irq(state->irq); +} + +static irqreturn_t adv7180_irq(int irq, void *devid) +{ + struct adv7180_state *state = devid; + + schedule_work(&state->work); + + disable_irq_nosync(state->irq); + + return IRQ_HANDLED; +} + +static int init_device(struct i2c_client *client, struct adv7180_state *state) +{ + int ret; + + /* Initialize adv7180 */ + /* Enable autodetection */ + if (state->autodetect) { + ret = + i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, + ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM + | state->input); + if (ret < 0) + return ret; + + ret = + i2c_smbus_write_byte_data(client, + ADV7180_AUTODETECT_ENABLE_REG, + ADV7180_AUTODETECT_DEFAULT); + if (ret < 0) + return ret; + } else { + ret = v4l2_std_to_adv7180(state->curr_norm); + if (ret < 0) + return ret; + + ret = + i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, + ret | state->input); + if (ret < 0) + return ret; + + } + /* ITU-R BT.656-4 compatible */ + ret = i2c_smbus_write_byte_data(client, + ADV7180_EXTENDED_OUTPUT_CONTROL_REG, + ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS); + if (ret < 0) + return ret; + + /* Manually set V bit end position in NTSC mode */ + ret = i2c_smbus_write_byte_data(client, + ADV7180_NTSC_V_BIT_END_REG, + ADV7180_NTSC_V_BIT_END_MANUAL_NVEND); + if (ret < 0) + return ret; + + /* read current norm */ + __adv7180_status(client, NULL, &state->curr_norm); + + /* register for interrupts */ + if (state->irq > 0) { + ret = request_irq(state->irq, adv7180_irq, 0, KBUILD_MODNAME, + state); + if (ret) + return ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, + ADV7180_ADI_CTRL_IRQ_SPACE); + if (ret < 0) + return ret; + + /* config the Interrupt pin to be active low */ + ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI, + ADV7180_ICONF1_ACTIVE_LOW | + ADV7180_ICONF1_PSYNC_ONLY); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0); + if (ret < 0) + return ret; + + /* enable AD change interrupts interrupts */ + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI, + ADV7180_IRQ3_AD_CHANGE); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, + 0); + if (ret < 0) + return ret; + } + + return 0; +} + +static __devinit int adv7180_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adv7180_state *state; + struct v4l2_subdev *sd; + int ret; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr, client->adapter->name); + + state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL); + if (state == NULL) { + ret = -ENOMEM; + goto err; + } + + state->irq = client->irq; + INIT_WORK(&state->work, adv7180_work); + mutex_init(&state->mutex); + state->autodetect = true; + state->input = 0; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &adv7180_ops); + + ret = adv7180_init_controls(state); + if (ret) + goto err_unreg_subdev; + ret = init_device(client, state); + if (ret) + goto err_free_ctrl; + return 0; + +err_free_ctrl: + adv7180_exit_controls(state); +err_unreg_subdev: + mutex_destroy(&state->mutex); + v4l2_device_unregister_subdev(sd); + kfree(state); +err: + printk(KERN_ERR KBUILD_MODNAME ": Failed to probe: %d\n", ret); + return ret; +} + +static __devexit int adv7180_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7180_state *state = to_state(sd); + + if (state->irq > 0) { + free_irq(client->irq, state); + if (cancel_work_sync(&state->work)) { + /* + * Work was pending, therefore we need to enable + * IRQ here to balance the disable_irq() done in the + * interrupt handler. + */ + enable_irq(state->irq); + } + } + + mutex_destroy(&state->mutex); + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + return 0; +} + +static const struct i2c_device_id adv7180_id[] = { + {KBUILD_MODNAME, 0}, + {}, +}; + +#ifdef CONFIG_PM +static int adv7180_suspend(struct i2c_client *client, pm_message_t state) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, + ADV7180_PWR_MAN_OFF); + if (ret < 0) + return ret; + return 0; +} + +static int adv7180_resume(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7180_state *state = to_state(sd); + int ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, + ADV7180_PWR_MAN_ON); + if (ret < 0) + return ret; + ret = init_device(client, state); + if (ret < 0) + return ret; + return 0; +} +#endif + +MODULE_DEVICE_TABLE(i2c, adv7180_id); + +static struct i2c_driver adv7180_driver = { + .driver = { + .owner = THIS_MODULE, + .name = KBUILD_MODNAME, + }, + .probe = adv7180_probe, + .remove = __devexit_p(adv7180_remove), +#ifdef CONFIG_PM + .suspend = adv7180_suspend, + .resume = adv7180_resume, +#endif + .id_table = adv7180_id, +}; + +module_i2c_driver(adv7180_driver); + +MODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver"); +MODULE_AUTHOR("Mocean Laboratories"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c new file mode 100644 index 00000000000..e1d4c89d714 --- /dev/null +++ b/drivers/media/i2c/adv7183.c @@ -0,0 +1,699 @@ +/* + * adv7183.c Analog Devices ADV7183 video decoder driver + * + * Copyright (c) 2011 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "adv7183_regs.h" + +struct adv7183 { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + + v4l2_std_id std; /* Current set standard */ + u32 input; + u32 output; + unsigned reset_pin; + unsigned oe_pin; + struct v4l2_mbus_framefmt fmt; +}; + +/* EXAMPLES USING 27 MHz CLOCK + * Mode 1 CVBS Input (Composite Video on AIN5) + * All standards are supported through autodetect, 8-bit, 4:2:2, ITU-R BT.656 output on P15 to P8. + */ +static const unsigned char adv7183_init_regs[] = { + ADV7183_IN_CTRL, 0x04, /* CVBS input on AIN5 */ + ADV7183_DIGI_CLAMP_CTRL_1, 0x00, /* Slow down digital clamps */ + ADV7183_SHAP_FILT_CTRL, 0x41, /* Set CSFM to SH1 */ + ADV7183_ADC_CTRL, 0x16, /* Power down ADC 1 and ADC 2 */ + ADV7183_CTI_DNR_CTRL_4, 0x04, /* Set DNR threshold to 4 for flat response */ + /* ADI recommended programming sequence */ + ADV7183_ADI_CTRL, 0x80, + ADV7183_CTI_DNR_CTRL_4, 0x20, + 0x52, 0x18, + 0x58, 0xED, + 0x77, 0xC5, + 0x7C, 0x93, + 0x7D, 0x00, + 0xD0, 0x48, + 0xD5, 0xA0, + 0xD7, 0xEA, + ADV7183_SD_SATURATION_CR, 0x3E, + ADV7183_PAL_V_END, 0x3E, + ADV7183_PAL_F_TOGGLE, 0x0F, + ADV7183_ADI_CTRL, 0x00, +}; + +static inline struct adv7183 *to_adv7183(struct v4l2_subdev *sd) +{ + return container_of(sd, struct adv7183, sd); +} +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct adv7183, hdl)->sd; +} + +static inline int adv7183_read(struct v4l2_subdev *sd, unsigned char reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_read_byte_data(client, reg); +} + +static inline int adv7183_write(struct v4l2_subdev *sd, unsigned char reg, + unsigned char value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_write_byte_data(client, reg, value); +} + +static int adv7183_writeregs(struct v4l2_subdev *sd, + const unsigned char *regs, unsigned int num) +{ + unsigned char reg, data; + unsigned int cnt = 0; + + if (num & 0x1) { + v4l2_err(sd, "invalid regs array\n"); + return -1; + } + + while (cnt < num) { + reg = *regs++; + data = *regs++; + cnt += 2; + + adv7183_write(sd, reg, data); + } + return 0; +} + +static int adv7183_log_status(struct v4l2_subdev *sd) +{ + struct adv7183 *decoder = to_adv7183(sd); + + v4l2_info(sd, "adv7183: Input control = 0x%02x\n", + adv7183_read(sd, ADV7183_IN_CTRL)); + v4l2_info(sd, "adv7183: Video selection = 0x%02x\n", + adv7183_read(sd, ADV7183_VD_SEL)); + v4l2_info(sd, "adv7183: Output control = 0x%02x\n", + adv7183_read(sd, ADV7183_OUT_CTRL)); + v4l2_info(sd, "adv7183: Extended output control = 0x%02x\n", + adv7183_read(sd, ADV7183_EXT_OUT_CTRL)); + v4l2_info(sd, "adv7183: Autodetect enable = 0x%02x\n", + adv7183_read(sd, ADV7183_AUTO_DET_EN)); + v4l2_info(sd, "adv7183: Contrast = 0x%02x\n", + adv7183_read(sd, ADV7183_CONTRAST)); + v4l2_info(sd, "adv7183: Brightness = 0x%02x\n", + adv7183_read(sd, ADV7183_BRIGHTNESS)); + v4l2_info(sd, "adv7183: Hue = 0x%02x\n", + adv7183_read(sd, ADV7183_HUE)); + v4l2_info(sd, "adv7183: Default value Y = 0x%02x\n", + adv7183_read(sd, ADV7183_DEF_Y)); + v4l2_info(sd, "adv7183: Default value C = 0x%02x\n", + adv7183_read(sd, ADV7183_DEF_C)); + v4l2_info(sd, "adv7183: ADI control = 0x%02x\n", + adv7183_read(sd, ADV7183_ADI_CTRL)); + v4l2_info(sd, "adv7183: Power Management = 0x%02x\n", + adv7183_read(sd, ADV7183_POW_MANAGE)); + v4l2_info(sd, "adv7183: Status 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_STATUS_1), + adv7183_read(sd, ADV7183_STATUS_2), + adv7183_read(sd, ADV7183_STATUS_3)); + v4l2_info(sd, "adv7183: Ident = 0x%02x\n", + adv7183_read(sd, ADV7183_IDENT)); + v4l2_info(sd, "adv7183: Analog clamp control = 0x%02x\n", + adv7183_read(sd, ADV7183_ANAL_CLAMP_CTRL)); + v4l2_info(sd, "adv7183: Digital clamp control 1 = 0x%02x\n", + adv7183_read(sd, ADV7183_DIGI_CLAMP_CTRL_1)); + v4l2_info(sd, "adv7183: Shaping filter control 1 and 2 = 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_SHAP_FILT_CTRL), + adv7183_read(sd, ADV7183_SHAP_FILT_CTRL_2)); + v4l2_info(sd, "adv7183: Comb filter control = 0x%02x\n", + adv7183_read(sd, ADV7183_COMB_FILT_CTRL)); + v4l2_info(sd, "adv7183: ADI control 2 = 0x%02x\n", + adv7183_read(sd, ADV7183_ADI_CTRL_2)); + v4l2_info(sd, "adv7183: Pixel delay control = 0x%02x\n", + adv7183_read(sd, ADV7183_PIX_DELAY_CTRL)); + v4l2_info(sd, "adv7183: Misc gain control = 0x%02x\n", + adv7183_read(sd, ADV7183_MISC_GAIN_CTRL)); + v4l2_info(sd, "adv7183: AGC mode control = 0x%02x\n", + adv7183_read(sd, ADV7183_AGC_MODE_CTRL)); + v4l2_info(sd, "adv7183: Chroma gain control 1 and 2 = 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_1), + adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_2)); + v4l2_info(sd, "adv7183: Luma gain control 1 and 2 = 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_1), + adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_2)); + v4l2_info(sd, "adv7183: Vsync field control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_VS_FIELD_CTRL_1), + adv7183_read(sd, ADV7183_VS_FIELD_CTRL_2), + adv7183_read(sd, ADV7183_VS_FIELD_CTRL_3)); + v4l2_info(sd, "adv7183: Hsync positon control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_HS_POS_CTRL_1), + adv7183_read(sd, ADV7183_HS_POS_CTRL_2), + adv7183_read(sd, ADV7183_HS_POS_CTRL_3)); + v4l2_info(sd, "adv7183: Polarity = 0x%02x\n", + adv7183_read(sd, ADV7183_POLARITY)); + v4l2_info(sd, "adv7183: ADC control = 0x%02x\n", + adv7183_read(sd, ADV7183_ADC_CTRL)); + v4l2_info(sd, "adv7183: SD offset Cb and Cr = 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_SD_OFFSET_CB), + adv7183_read(sd, ADV7183_SD_OFFSET_CR)); + v4l2_info(sd, "adv7183: SD saturation Cb and Cr = 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_SD_SATURATION_CB), + adv7183_read(sd, ADV7183_SD_SATURATION_CR)); + v4l2_info(sd, "adv7183: Drive strength = 0x%02x\n", + adv7183_read(sd, ADV7183_DRIVE_STR)); + v4l2_ctrl_handler_log_status(&decoder->hdl, sd->name); + return 0; +} + +static int adv7183_g_std(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + struct adv7183 *decoder = to_adv7183(sd); + + *std = decoder->std; + return 0; +} + +static int adv7183_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7183 *decoder = to_adv7183(sd); + int reg; + + reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF; + if (std == V4L2_STD_PAL_60) + reg |= 0x60; + else if (std == V4L2_STD_NTSC_443) + reg |= 0x70; + else if (std == V4L2_STD_PAL_N) + reg |= 0x90; + else if (std == V4L2_STD_PAL_M) + reg |= 0xA0; + else if (std == V4L2_STD_PAL_Nc) + reg |= 0xC0; + else if (std & V4L2_STD_PAL) + reg |= 0x80; + else if (std & V4L2_STD_NTSC) + reg |= 0x50; + else if (std & V4L2_STD_SECAM) + reg |= 0xE0; + else + return -EINVAL; + adv7183_write(sd, ADV7183_IN_CTRL, reg); + + decoder->std = std; + + return 0; +} + +static int adv7183_reset(struct v4l2_subdev *sd, u32 val) +{ + int reg; + + reg = adv7183_read(sd, ADV7183_POW_MANAGE) | 0x80; + adv7183_write(sd, ADV7183_POW_MANAGE, reg); + /* wait 5ms before any further i2c writes are performed */ + usleep_range(5000, 10000); + return 0; +} + +static int adv7183_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct adv7183 *decoder = to_adv7183(sd); + int reg; + + if ((input > ADV7183_COMPONENT1) || (output > ADV7183_16BIT_OUT)) + return -EINVAL; + + if (input != decoder->input) { + decoder->input = input; + reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF0; + switch (input) { + case ADV7183_COMPOSITE1: + reg |= 0x1; + break; + case ADV7183_COMPOSITE2: + reg |= 0x2; + break; + case ADV7183_COMPOSITE3: + reg |= 0x3; + break; + case ADV7183_COMPOSITE4: + reg |= 0x4; + break; + case ADV7183_COMPOSITE5: + reg |= 0x5; + break; + case ADV7183_COMPOSITE6: + reg |= 0xB; + break; + case ADV7183_COMPOSITE7: + reg |= 0xC; + break; + case ADV7183_COMPOSITE8: + reg |= 0xD; + break; + case ADV7183_COMPOSITE9: + reg |= 0xE; + break; + case ADV7183_COMPOSITE10: + reg |= 0xF; + break; + case ADV7183_SVIDEO0: + reg |= 0x6; + break; + case ADV7183_SVIDEO1: + reg |= 0x7; + break; + case ADV7183_SVIDEO2: + reg |= 0x8; + break; + case ADV7183_COMPONENT0: + reg |= 0x9; + break; + case ADV7183_COMPONENT1: + reg |= 0xA; + break; + default: + break; + } + adv7183_write(sd, ADV7183_IN_CTRL, reg); + } + + if (output != decoder->output) { + decoder->output = output; + reg = adv7183_read(sd, ADV7183_OUT_CTRL) & 0xC0; + switch (output) { + case ADV7183_16BIT_OUT: + reg |= 0x9; + break; + default: + reg |= 0xC; + break; + } + adv7183_write(sd, ADV7183_OUT_CTRL, reg); + } + + return 0; +} + +static int adv7183_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + int val = ctrl->val; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if (val < 0) + val = 127 - val; + adv7183_write(sd, ADV7183_BRIGHTNESS, val); + break; + case V4L2_CID_CONTRAST: + adv7183_write(sd, ADV7183_CONTRAST, val); + break; + case V4L2_CID_SATURATION: + adv7183_write(sd, ADV7183_SD_SATURATION_CB, val >> 8); + adv7183_write(sd, ADV7183_SD_SATURATION_CR, (val & 0xFF)); + break; + case V4L2_CID_HUE: + adv7183_write(sd, ADV7183_SD_OFFSET_CB, val >> 8); + adv7183_write(sd, ADV7183_SD_OFFSET_CR, (val & 0xFF)); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int adv7183_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + struct adv7183 *decoder = to_adv7183(sd); + int reg; + + /* enable autodetection block */ + reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF; + adv7183_write(sd, ADV7183_IN_CTRL, reg); + + /* wait autodetection switch */ + mdelay(10); + + /* get autodetection result */ + reg = adv7183_read(sd, ADV7183_STATUS_1); + switch ((reg >> 0x4) & 0x7) { + case 0: + *std = V4L2_STD_NTSC; + break; + case 1: + *std = V4L2_STD_NTSC_443; + break; + case 2: + *std = V4L2_STD_PAL_M; + break; + case 3: + *std = V4L2_STD_PAL_60; + break; + case 4: + *std = V4L2_STD_PAL; + break; + case 5: + *std = V4L2_STD_SECAM; + break; + case 6: + *std = V4L2_STD_PAL_Nc; + break; + case 7: + *std = V4L2_STD_SECAM; + break; + default: + *std = V4L2_STD_UNKNOWN; + break; + } + + /* after std detection, write back user set std */ + adv7183_s_std(sd, decoder->std); + return 0; +} + +static int adv7183_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + int reg; + + *status = V4L2_IN_ST_NO_SIGNAL; + reg = adv7183_read(sd, ADV7183_STATUS_1); + if (reg < 0) + return reg; + if (reg & 0x1) + *status = 0; + return 0; +} + +static int adv7183_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, + enum v4l2_mbus_pixelcode *code) +{ + if (index > 0) + return -EINVAL; + + *code = V4L2_MBUS_FMT_UYVY8_2X8; + return 0; +} + +static int adv7183_try_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct adv7183 *decoder = to_adv7183(sd); + + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + if (decoder->std & V4L2_STD_525_60) { + fmt->field = V4L2_FIELD_SEQ_TB; + fmt->width = 720; + fmt->height = 480; + } else { + fmt->field = V4L2_FIELD_SEQ_BT; + fmt->width = 720; + fmt->height = 576; + } + return 0; +} + +static int adv7183_s_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct adv7183 *decoder = to_adv7183(sd); + + adv7183_try_mbus_fmt(sd, fmt); + decoder->fmt = *fmt; + return 0; +} + +static int adv7183_g_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct adv7183 *decoder = to_adv7183(sd); + + *fmt = decoder->fmt; + return 0; +} + +static int adv7183_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct adv7183 *decoder = to_adv7183(sd); + + if (enable) + gpio_direction_output(decoder->oe_pin, 0); + else + gpio_direction_output(decoder->oe_pin, 1); + udelay(1); + return 0; +} + +static int adv7183_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + int rev; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + /* 0x11 for adv7183, 0x13 for adv7183b */ + rev = adv7183_read(sd, ADV7183_IDENT); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7183, rev); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int adv7183_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = adv7183_read(sd, reg->reg & 0xff); + reg->size = 1; + return 0; +} + +static int adv7183_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + adv7183_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif + +static const struct v4l2_ctrl_ops adv7183_ctrl_ops = { + .s_ctrl = adv7183_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops adv7183_core_ops = { + .log_status = adv7183_log_status, + .g_std = adv7183_g_std, + .s_std = adv7183_s_std, + .reset = adv7183_reset, + .g_chip_ident = adv7183_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = adv7183_g_register, + .s_register = adv7183_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops adv7183_video_ops = { + .s_routing = adv7183_s_routing, + .querystd = adv7183_querystd, + .g_input_status = adv7183_g_input_status, + .enum_mbus_fmt = adv7183_enum_mbus_fmt, + .try_mbus_fmt = adv7183_try_mbus_fmt, + .s_mbus_fmt = adv7183_s_mbus_fmt, + .g_mbus_fmt = adv7183_g_mbus_fmt, + .s_stream = adv7183_s_stream, +}; + +static const struct v4l2_subdev_ops adv7183_ops = { + .core = &adv7183_core_ops, + .video = &adv7183_video_ops, +}; + +static int adv7183_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adv7183 *decoder; + struct v4l2_subdev *sd; + struct v4l2_ctrl_handler *hdl; + int ret; + struct v4l2_mbus_framefmt fmt; + const unsigned *pin_array; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + pin_array = client->dev.platform_data; + if (pin_array == NULL) + return -EINVAL; + + decoder = kzalloc(sizeof(struct adv7183), GFP_KERNEL); + if (decoder == NULL) + return -ENOMEM; + + decoder->reset_pin = pin_array[0]; + decoder->oe_pin = pin_array[1]; + + if (gpio_request(decoder->reset_pin, "ADV7183 Reset")) { + v4l_err(client, "failed to request GPIO %d\n", decoder->reset_pin); + ret = -EBUSY; + goto err_free_decoder; + } + + if (gpio_request(decoder->oe_pin, "ADV7183 Output Enable")) { + v4l_err(client, "failed to request GPIO %d\n", decoder->oe_pin); + ret = -EBUSY; + goto err_free_reset; + } + + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &adv7183_ops); + + hdl = &decoder->hdl; + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, + V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); + v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, + V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x80); + v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, + V4L2_CID_SATURATION, 0, 0xFFFF, 1, 0x8080); + v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, + V4L2_CID_HUE, 0, 0xFFFF, 1, 0x8080); + /* hook the control handler into the driver */ + sd->ctrl_handler = hdl; + if (hdl->error) { + ret = hdl->error; + + v4l2_ctrl_handler_free(hdl); + goto err_free_oe; + } + + /* v4l2 doesn't support an autodetect standard, pick PAL as default */ + decoder->std = V4L2_STD_PAL; + decoder->input = ADV7183_COMPOSITE4; + decoder->output = ADV7183_8BIT_OUT; + + gpio_direction_output(decoder->oe_pin, 1); + /* reset chip */ + gpio_direction_output(decoder->reset_pin, 0); + /* reset pulse width at least 5ms */ + mdelay(10); + gpio_direction_output(decoder->reset_pin, 1); + /* wait 5ms before any further i2c writes are performed */ + mdelay(5); + + adv7183_writeregs(sd, adv7183_init_regs, ARRAY_SIZE(adv7183_init_regs)); + adv7183_s_std(sd, decoder->std); + fmt.width = 720; + fmt.height = 576; + adv7183_s_mbus_fmt(sd, &fmt); + + /* initialize the hardware to the default control values */ + ret = v4l2_ctrl_handler_setup(hdl); + if (ret) { + v4l2_ctrl_handler_free(hdl); + goto err_free_oe; + } + + return 0; +err_free_oe: + gpio_free(decoder->oe_pin); +err_free_reset: + gpio_free(decoder->reset_pin); +err_free_decoder: + kfree(decoder); + return ret; +} + +static int adv7183_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7183 *decoder = to_adv7183(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); + gpio_free(decoder->oe_pin); + gpio_free(decoder->reset_pin); + kfree(decoder); + return 0; +} + +static const struct i2c_device_id adv7183_id[] = { + {"adv7183", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, adv7183_id); + +static struct i2c_driver adv7183_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "adv7183", + }, + .probe = adv7183_probe, + .remove = __devexit_p(adv7183_remove), + .id_table = adv7183_id, +}; + +static __init int adv7183_init(void) +{ + return i2c_add_driver(&adv7183_driver); +} + +static __exit void adv7183_exit(void) +{ + i2c_del_driver(&adv7183_driver); +} + +module_init(adv7183_init); +module_exit(adv7183_exit); + +MODULE_DESCRIPTION("Analog Devices ADV7183 video decoder driver"); +MODULE_AUTHOR("Scott Jiang "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/adv7183_regs.h b/drivers/media/i2c/adv7183_regs.h new file mode 100644 index 00000000000..4a5b7d211d2 --- /dev/null +++ b/drivers/media/i2c/adv7183_regs.h @@ -0,0 +1,107 @@ +/* + * adv7183 - Analog Devices ADV7183 video decoder registers + * + * Copyright (c) 2011 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + */ + +#ifndef _ADV7183_REGS_H_ +#define _ADV7183_REGS_H_ + +#define ADV7183_IN_CTRL 0x00 /* Input control */ +#define ADV7183_VD_SEL 0x01 /* Video selection */ +#define ADV7183_OUT_CTRL 0x03 /* Output control */ +#define ADV7183_EXT_OUT_CTRL 0x04 /* Extended output control */ +#define ADV7183_AUTO_DET_EN 0x07 /* Autodetect enable */ +#define ADV7183_CONTRAST 0x08 /* Contrast */ +#define ADV7183_BRIGHTNESS 0x0A /* Brightness */ +#define ADV7183_HUE 0x0B /* Hue */ +#define ADV7183_DEF_Y 0x0C /* Default value Y */ +#define ADV7183_DEF_C 0x0D /* Default value C */ +#define ADV7183_ADI_CTRL 0x0E /* ADI control */ +#define ADV7183_POW_MANAGE 0x0F /* Power Management */ +#define ADV7183_STATUS_1 0x10 /* Status 1 */ +#define ADV7183_IDENT 0x11 /* Ident */ +#define ADV7183_STATUS_2 0x12 /* Status 2 */ +#define ADV7183_STATUS_3 0x13 /* Status 3 */ +#define ADV7183_ANAL_CLAMP_CTRL 0x14 /* Analog clamp control */ +#define ADV7183_DIGI_CLAMP_CTRL_1 0x15 /* Digital clamp control 1 */ +#define ADV7183_SHAP_FILT_CTRL 0x17 /* Shaping filter control */ +#define ADV7183_SHAP_FILT_CTRL_2 0x18 /* Shaping filter control 2 */ +#define ADV7183_COMB_FILT_CTRL 0x19 /* Comb filter control */ +#define ADV7183_ADI_CTRL_2 0x1D /* ADI control 2 */ +#define ADV7183_PIX_DELAY_CTRL 0x27 /* Pixel delay control */ +#define ADV7183_MISC_GAIN_CTRL 0x2B /* Misc gain control */ +#define ADV7183_AGC_MODE_CTRL 0x2C /* AGC mode control */ +#define ADV7183_CHRO_GAIN_CTRL_1 0x2D /* Chroma gain control 1 */ +#define ADV7183_CHRO_GAIN_CTRL_2 0x2E /* Chroma gain control 2 */ +#define ADV7183_LUMA_GAIN_CTRL_1 0x2F /* Luma gain control 1 */ +#define ADV7183_LUMA_GAIN_CTRL_2 0x30 /* Luma gain control 2 */ +#define ADV7183_VS_FIELD_CTRL_1 0x31 /* Vsync field control 1 */ +#define ADV7183_VS_FIELD_CTRL_2 0x32 /* Vsync field control 2 */ +#define ADV7183_VS_FIELD_CTRL_3 0x33 /* Vsync field control 3 */ +#define ADV7183_HS_POS_CTRL_1 0x34 /* Hsync positon control 1 */ +#define ADV7183_HS_POS_CTRL_2 0x35 /* Hsync positon control 2 */ +#define ADV7183_HS_POS_CTRL_3 0x36 /* Hsync positon control 3 */ +#define ADV7183_POLARITY 0x37 /* Polarity */ +#define ADV7183_NTSC_COMB_CTRL 0x38 /* NTSC comb control */ +#define ADV7183_PAL_COMB_CTRL 0x39 /* PAL comb control */ +#define ADV7183_ADC_CTRL 0x3A /* ADC control */ +#define ADV7183_MAN_WIN_CTRL 0x3D /* Manual window control */ +#define ADV7183_RESAMPLE_CTRL 0x41 /* Resample control */ +#define ADV7183_GEMSTAR_CTRL_1 0x48 /* Gemstar ctrl 1 */ +#define ADV7183_GEMSTAR_CTRL_2 0x49 /* Gemstar ctrl 2 */ +#define ADV7183_GEMSTAR_CTRL_3 0x4A /* Gemstar ctrl 3 */ +#define ADV7183_GEMSTAR_CTRL_4 0x4B /* Gemstar ctrl 4 */ +#define ADV7183_GEMSTAR_CTRL_5 0x4C /* Gemstar ctrl 5 */ +#define ADV7183_CTI_DNR_CTRL_1 0x4D /* CTI DNR ctrl 1 */ +#define ADV7183_CTI_DNR_CTRL_2 0x4E /* CTI DNR ctrl 2 */ +#define ADV7183_CTI_DNR_CTRL_4 0x50 /* CTI DNR ctrl 4 */ +#define ADV7183_LOCK_CNT 0x51 /* Lock count */ +#define ADV7183_FREE_LINE_LEN 0x8F /* Free-Run line length 1 */ +#define ADV7183_VBI_INFO 0x90 /* VBI info */ +#define ADV7183_WSS_1 0x91 /* WSS 1 */ +#define ADV7183_WSS_2 0x92 /* WSS 2 */ +#define ADV7183_EDTV_1 0x93 /* EDTV 1 */ +#define ADV7183_EDTV_2 0x94 /* EDTV 2 */ +#define ADV7183_EDTV_3 0x95 /* EDTV 3 */ +#define ADV7183_CGMS_1 0x96 /* CGMS 1 */ +#define ADV7183_CGMS_2 0x97 /* CGMS 2 */ +#define ADV7183_CGMS_3 0x98 /* CGMS 3 */ +#define ADV7183_CCAP_1 0x99 /* CCAP 1 */ +#define ADV7183_CCAP_2 0x9A /* CCAP 2 */ +#define ADV7183_LETTERBOX_1 0x9B /* Letterbox 1 */ +#define ADV7183_LETTERBOX_2 0x9C /* Letterbox 2 */ +#define ADV7183_LETTERBOX_3 0x9D /* Letterbox 3 */ +#define ADV7183_CRC_EN 0xB2 /* CRC enable */ +#define ADV7183_ADC_SWITCH_1 0xC3 /* ADC switch 1 */ +#define ADV7183_ADC_SWITCH_2 0xC4 /* ADC swithc 2 */ +#define ADV7183_LETTERBOX_CTRL_1 0xDC /* Letterbox control 1 */ +#define ADV7183_LETTERBOX_CTRL_2 0xDD /* Letterbox control 2 */ +#define ADV7183_SD_OFFSET_CB 0xE1 /* SD offset Cb */ +#define ADV7183_SD_OFFSET_CR 0xE2 /* SD offset Cr */ +#define ADV7183_SD_SATURATION_CB 0xE3 /* SD saturation Cb */ +#define ADV7183_SD_SATURATION_CR 0xE4 /* SD saturation Cr */ +#define ADV7183_NTSC_V_BEGIN 0xE5 /* NTSC V bit begin */ +#define ADV7183_NTSC_V_END 0xE6 /* NTSC V bit end */ +#define ADV7183_NTSC_F_TOGGLE 0xE7 /* NTSC F bit toggle */ +#define ADV7183_PAL_V_BEGIN 0xE8 /* PAL V bit begin */ +#define ADV7183_PAL_V_END 0xE9 /* PAL V bit end */ +#define ADV7183_PAL_F_TOGGLE 0xEA /* PAL F bit toggle */ +#define ADV7183_DRIVE_STR 0xF4 /* Drive strength */ +#define ADV7183_IF_COMP_CTRL 0xF8 /* IF comp control */ +#define ADV7183_VS_MODE_CTRL 0xF9 /* VS mode control */ + +#endif diff --git a/drivers/media/i2c/adv7343.c b/drivers/media/i2c/adv7343.c new file mode 100644 index 00000000000..2b5aa676a84 --- /dev/null +++ b/drivers/media/i2c/adv7343.c @@ -0,0 +1,476 @@ +/* + * adv7343 - ADV7343 Video Encoder Driver + * + * The encoder hardware does not support SECAM. + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "adv7343_regs.h" + +MODULE_DESCRIPTION("ADV7343 video encoder driver"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level 0-1"); + +struct adv7343_state { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + u8 reg00; + u8 reg01; + u8 reg02; + u8 reg35; + u8 reg80; + u8 reg82; + u32 output; + v4l2_std_id std; +}; + +static inline struct adv7343_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct adv7343_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct adv7343_state, hdl)->sd; +} + +static inline int adv7343_write(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_write_byte_data(client, reg, value); +} + +static const u8 adv7343_init_reg_val[] = { + ADV7343_SOFT_RESET, ADV7343_SOFT_RESET_DEFAULT, + ADV7343_POWER_MODE_REG, ADV7343_POWER_MODE_REG_DEFAULT, + + ADV7343_HD_MODE_REG1, ADV7343_HD_MODE_REG1_DEFAULT, + ADV7343_HD_MODE_REG2, ADV7343_HD_MODE_REG2_DEFAULT, + ADV7343_HD_MODE_REG3, ADV7343_HD_MODE_REG3_DEFAULT, + ADV7343_HD_MODE_REG4, ADV7343_HD_MODE_REG4_DEFAULT, + ADV7343_HD_MODE_REG5, ADV7343_HD_MODE_REG5_DEFAULT, + ADV7343_HD_MODE_REG6, ADV7343_HD_MODE_REG6_DEFAULT, + ADV7343_HD_MODE_REG7, ADV7343_HD_MODE_REG7_DEFAULT, + + ADV7343_SD_MODE_REG1, ADV7343_SD_MODE_REG1_DEFAULT, + ADV7343_SD_MODE_REG2, ADV7343_SD_MODE_REG2_DEFAULT, + ADV7343_SD_MODE_REG3, ADV7343_SD_MODE_REG3_DEFAULT, + ADV7343_SD_MODE_REG4, ADV7343_SD_MODE_REG4_DEFAULT, + ADV7343_SD_MODE_REG5, ADV7343_SD_MODE_REG5_DEFAULT, + ADV7343_SD_MODE_REG6, ADV7343_SD_MODE_REG6_DEFAULT, + ADV7343_SD_MODE_REG7, ADV7343_SD_MODE_REG7_DEFAULT, + ADV7343_SD_MODE_REG8, ADV7343_SD_MODE_REG8_DEFAULT, + + ADV7343_SD_HUE_REG, ADV7343_SD_HUE_REG_DEFAULT, + ADV7343_SD_CGMS_WSS0, ADV7343_SD_CGMS_WSS0_DEFAULT, + ADV7343_SD_BRIGHTNESS_WSS, ADV7343_SD_BRIGHTNESS_WSS_DEFAULT, +}; + +/* + * 2^32 + * FSC(reg) = FSC (HZ) * -------- + * 27000000 + */ +static const struct adv7343_std_info stdinfo[] = { + { + /* FSC(Hz) = 3,579,545.45 Hz */ + SD_STD_NTSC, 569408542, V4L2_STD_NTSC, + }, { + /* FSC(Hz) = 3,575,611.00 Hz */ + SD_STD_PAL_M, 568782678, V4L2_STD_PAL_M, + }, { + /* FSC(Hz) = 3,582,056.00 */ + SD_STD_PAL_N, 569807903, V4L2_STD_PAL_Nc, + }, { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_PAL_N, 705268427, V4L2_STD_PAL_N, + }, { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_PAL_BDGHI, 705268427, V4L2_STD_PAL, + }, { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_NTSC, 705268427, V4L2_STD_NTSC_443, + }, { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_PAL_M, 705268427, V4L2_STD_PAL_60, + }, +}; + +static int adv7343_setstd(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7343_state *state = to_state(sd); + struct adv7343_std_info *std_info; + int num_std; + char *fsc_ptr; + u8 reg, val; + int err = 0; + int i = 0; + + std_info = (struct adv7343_std_info *)stdinfo; + num_std = ARRAY_SIZE(stdinfo); + + for (i = 0; i < num_std; i++) { + if (std_info[i].stdid & std) + break; + } + + if (i == num_std) { + v4l2_dbg(1, debug, sd, + "Invalid std or std is not supported: %llx\n", + (unsigned long long)std); + return -EINVAL; + } + + /* Set the standard */ + val = state->reg80 & (~(SD_STD_MASK)); + val |= std_info[i].standard_val3; + err = adv7343_write(sd, ADV7343_SD_MODE_REG1, val); + if (err < 0) + goto setstd_exit; + + state->reg80 = val; + + /* Configure the input mode register */ + val = state->reg01 & (~((u8) INPUT_MODE_MASK)); + val |= SD_INPUT_MODE; + err = adv7343_write(sd, ADV7343_MODE_SELECT_REG, val); + if (err < 0) + goto setstd_exit; + + state->reg01 = val; + + /* Program the sub carrier frequency registers */ + fsc_ptr = (unsigned char *)&std_info[i].fsc_val; + reg = ADV7343_FSC_REG0; + for (i = 0; i < 4; i++, reg++, fsc_ptr++) { + err = adv7343_write(sd, reg, *fsc_ptr); + if (err < 0) + goto setstd_exit; + } + + val = state->reg80; + + /* Filter settings */ + if (std & (V4L2_STD_NTSC | V4L2_STD_NTSC_443)) + val &= 0x03; + else if (std & ~V4L2_STD_SECAM) + val |= 0x04; + + err = adv7343_write(sd, ADV7343_SD_MODE_REG1, val); + if (err < 0) + goto setstd_exit; + + state->reg80 = val; + +setstd_exit: + if (err != 0) + v4l2_err(sd, "Error setting std, write failed\n"); + + return err; +} + +static int adv7343_setoutput(struct v4l2_subdev *sd, u32 output_type) +{ + struct adv7343_state *state = to_state(sd); + unsigned char val; + int err = 0; + + if (output_type > ADV7343_SVIDEO_ID) { + v4l2_dbg(1, debug, sd, + "Invalid output type or output type not supported:%d\n", + output_type); + return -EINVAL; + } + + /* Enable Appropriate DAC */ + val = state->reg00 & 0x03; + + if (output_type == ADV7343_COMPOSITE_ID) + val |= ADV7343_COMPOSITE_POWER_VALUE; + else if (output_type == ADV7343_COMPONENT_ID) + val |= ADV7343_COMPONENT_POWER_VALUE; + else + val |= ADV7343_SVIDEO_POWER_VALUE; + + err = adv7343_write(sd, ADV7343_POWER_MODE_REG, val); + if (err < 0) + goto setoutput_exit; + + state->reg00 = val; + + /* Enable YUV output */ + val = state->reg02 | YUV_OUTPUT_SELECT; + err = adv7343_write(sd, ADV7343_MODE_REG0, val); + if (err < 0) + goto setoutput_exit; + + state->reg02 = val; + + /* configure SD DAC Output 2 and SD DAC Output 1 bit to zero */ + val = state->reg82 & (SD_DAC_1_DI & SD_DAC_2_DI); + err = adv7343_write(sd, ADV7343_SD_MODE_REG2, val); + if (err < 0) + goto setoutput_exit; + + state->reg82 = val; + + /* configure ED/HD Color DAC Swap and ED/HD RGB Input Enable bit to + * zero */ + val = state->reg35 & (HD_RGB_INPUT_DI & HD_DAC_SWAP_DI); + err = adv7343_write(sd, ADV7343_HD_MODE_REG6, val); + if (err < 0) + goto setoutput_exit; + + state->reg35 = val; + +setoutput_exit: + if (err != 0) + v4l2_err(sd, "Error setting output, write failed\n"); + + return err; +} + +static int adv7343_log_status(struct v4l2_subdev *sd) +{ + struct adv7343_state *state = to_state(sd); + + v4l2_info(sd, "Standard: %llx\n", (unsigned long long)state->std); + v4l2_info(sd, "Output: %s\n", (state->output == 0) ? "Composite" : + ((state->output == 1) ? "Component" : "S-Video")); + return 0; +} + +static int adv7343_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + return adv7343_write(sd, ADV7343_SD_BRIGHTNESS_WSS, + ctrl->val); + + case V4L2_CID_HUE: + return adv7343_write(sd, ADV7343_SD_HUE_REG, ctrl->val); + + case V4L2_CID_GAIN: + return adv7343_write(sd, ADV7343_DAC2_OUTPUT_LEVEL, ctrl->val); + } + return -EINVAL; +} + +static int adv7343_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7343, 0); +} + +static const struct v4l2_ctrl_ops adv7343_ctrl_ops = { + .s_ctrl = adv7343_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops adv7343_core_ops = { + .log_status = adv7343_log_status, + .g_chip_ident = adv7343_g_chip_ident, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, +}; + +static int adv7343_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7343_state *state = to_state(sd); + int err = 0; + + if (state->std == std) + return 0; + + err = adv7343_setstd(sd, std); + if (!err) + state->std = std; + + return err; +} + +static int adv7343_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct adv7343_state *state = to_state(sd); + int err = 0; + + if (state->output == output) + return 0; + + err = adv7343_setoutput(sd, output); + if (!err) + state->output = output; + + return err; +} + +static const struct v4l2_subdev_video_ops adv7343_video_ops = { + .s_std_output = adv7343_s_std_output, + .s_routing = adv7343_s_routing, +}; + +static const struct v4l2_subdev_ops adv7343_ops = { + .core = &adv7343_core_ops, + .video = &adv7343_video_ops, +}; + +static int adv7343_initialize(struct v4l2_subdev *sd) +{ + struct adv7343_state *state = to_state(sd); + int err = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(adv7343_init_reg_val); i += 2) { + + err = adv7343_write(sd, adv7343_init_reg_val[i], + adv7343_init_reg_val[i+1]); + if (err) { + v4l2_err(sd, "Error initializing\n"); + return err; + } + } + + /* Configure for default video standard */ + err = adv7343_setoutput(sd, state->output); + if (err < 0) { + v4l2_err(sd, "Error setting output during init\n"); + return -EINVAL; + } + + err = adv7343_setstd(sd, state->std); + if (err < 0) { + v4l2_err(sd, "Error setting std during init\n"); + return -EINVAL; + } + + return err; +} + +static int adv7343_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adv7343_state *state; + int err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct adv7343_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + + state->reg00 = 0x80; + state->reg01 = 0x00; + state->reg02 = 0x20; + state->reg35 = 0x00; + state->reg80 = ADV7343_SD_MODE_REG1_DEFAULT; + state->reg82 = ADV7343_SD_MODE_REG2_DEFAULT; + + state->output = ADV7343_COMPOSITE_ID; + state->std = V4L2_STD_NTSC; + + v4l2_i2c_subdev_init(&state->sd, client, &adv7343_ops); + + v4l2_ctrl_handler_init(&state->hdl, 2); + v4l2_ctrl_new_std(&state->hdl, &adv7343_ctrl_ops, + V4L2_CID_BRIGHTNESS, ADV7343_BRIGHTNESS_MIN, + ADV7343_BRIGHTNESS_MAX, 1, + ADV7343_BRIGHTNESS_DEF); + v4l2_ctrl_new_std(&state->hdl, &adv7343_ctrl_ops, + V4L2_CID_HUE, ADV7343_HUE_MIN, + ADV7343_HUE_MAX, 1, + ADV7343_HUE_DEF); + v4l2_ctrl_new_std(&state->hdl, &adv7343_ctrl_ops, + V4L2_CID_GAIN, ADV7343_GAIN_MIN, + ADV7343_GAIN_MAX, 1, + ADV7343_GAIN_DEF); + state->sd.ctrl_handler = &state->hdl; + if (state->hdl.error) { + int err = state->hdl.error; + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + v4l2_ctrl_handler_setup(&state->hdl); + + err = adv7343_initialize(&state->sd); + if (err) { + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + } + return err; +} + +static int adv7343_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7343_state *state = to_state(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + + return 0; +} + +static const struct i2c_device_id adv7343_id[] = { + {"adv7343", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, adv7343_id); + +static struct i2c_driver adv7343_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "adv7343", + }, + .probe = adv7343_probe, + .remove = adv7343_remove, + .id_table = adv7343_id, +}; + +module_i2c_driver(adv7343_driver); diff --git a/drivers/media/i2c/adv7343_regs.h b/drivers/media/i2c/adv7343_regs.h new file mode 100644 index 00000000000..44660676434 --- /dev/null +++ b/drivers/media/i2c/adv7343_regs.h @@ -0,0 +1,181 @@ +/* + * ADV7343 encoder related structure and register definitions + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ADV7343_REG_H +#define ADV7343_REGS_H + +struct adv7343_std_info { + u32 standard_val3; + u32 fsc_val; + v4l2_std_id stdid; +}; + +/* Register offset macros */ +#define ADV7343_POWER_MODE_REG (0x00) +#define ADV7343_MODE_SELECT_REG (0x01) +#define ADV7343_MODE_REG0 (0x02) + +#define ADV7343_DAC2_OUTPUT_LEVEL (0x0b) + +#define ADV7343_SOFT_RESET (0x17) + +#define ADV7343_HD_MODE_REG1 (0x30) +#define ADV7343_HD_MODE_REG2 (0x31) +#define ADV7343_HD_MODE_REG3 (0x32) +#define ADV7343_HD_MODE_REG4 (0x33) +#define ADV7343_HD_MODE_REG5 (0x34) +#define ADV7343_HD_MODE_REG6 (0x35) + +#define ADV7343_HD_MODE_REG7 (0x39) + +#define ADV7343_SD_MODE_REG1 (0x80) +#define ADV7343_SD_MODE_REG2 (0x82) +#define ADV7343_SD_MODE_REG3 (0x83) +#define ADV7343_SD_MODE_REG4 (0x84) +#define ADV7343_SD_MODE_REG5 (0x86) +#define ADV7343_SD_MODE_REG6 (0x87) +#define ADV7343_SD_MODE_REG7 (0x88) +#define ADV7343_SD_MODE_REG8 (0x89) + +#define ADV7343_FSC_REG0 (0x8C) +#define ADV7343_FSC_REG1 (0x8D) +#define ADV7343_FSC_REG2 (0x8E) +#define ADV7343_FSC_REG3 (0x8F) + +#define ADV7343_SD_CGMS_WSS0 (0x99) + +#define ADV7343_SD_HUE_REG (0xA0) +#define ADV7343_SD_BRIGHTNESS_WSS (0xA1) + +/* Default values for the registers */ +#define ADV7343_POWER_MODE_REG_DEFAULT (0x10) +#define ADV7343_HD_MODE_REG1_DEFAULT (0x3C) /* Changed Default + 720p EAVSAV code*/ +#define ADV7343_HD_MODE_REG2_DEFAULT (0x01) /* Changed Pixel data + valid */ +#define ADV7343_HD_MODE_REG3_DEFAULT (0x00) /* Color delay 0 clks */ +#define ADV7343_HD_MODE_REG4_DEFAULT (0xE8) /* Changed */ +#define ADV7343_HD_MODE_REG5_DEFAULT (0x08) +#define ADV7343_HD_MODE_REG6_DEFAULT (0x00) +#define ADV7343_HD_MODE_REG7_DEFAULT (0x00) +#define ADV7343_SD_MODE_REG8_DEFAULT (0x00) +#define ADV7343_SOFT_RESET_DEFAULT (0x02) +#define ADV7343_COMPOSITE_POWER_VALUE (0x80) +#define ADV7343_COMPONENT_POWER_VALUE (0x1C) +#define ADV7343_SVIDEO_POWER_VALUE (0x60) +#define ADV7343_SD_HUE_REG_DEFAULT (127) +#define ADV7343_SD_BRIGHTNESS_WSS_DEFAULT (0x03) + +#define ADV7343_SD_CGMS_WSS0_DEFAULT (0x10) + +#define ADV7343_SD_MODE_REG1_DEFAULT (0x00) +#define ADV7343_SD_MODE_REG2_DEFAULT (0xC9) +#define ADV7343_SD_MODE_REG3_DEFAULT (0x10) +#define ADV7343_SD_MODE_REG4_DEFAULT (0x01) +#define ADV7343_SD_MODE_REG5_DEFAULT (0x02) +#define ADV7343_SD_MODE_REG6_DEFAULT (0x0C) +#define ADV7343_SD_MODE_REG7_DEFAULT (0x04) +#define ADV7343_SD_MODE_REG8_DEFAULT (0x00) + +/* Bit masks for Mode Select Register */ +#define INPUT_MODE_MASK (0x70) +#define SD_INPUT_MODE (0x00) +#define HD_720P_INPUT_MODE (0x10) +#define HD_1080I_INPUT_MODE (0x10) + +/* Bit masks for Mode Register 0 */ +#define TEST_PATTERN_BLACK_BAR_EN (0x04) +#define YUV_OUTPUT_SELECT (0x20) +#define RGB_OUTPUT_SELECT (0xDF) + +/* Bit masks for DAC output levels */ +#define DAC_OUTPUT_LEVEL_MASK (0xFF) + +/* Bit masks for soft reset register */ +#define SOFT_RESET (0x02) + +/* Bit masks for HD Mode Register 1 */ +#define OUTPUT_STD_MASK (0x03) +#define OUTPUT_STD_SHIFT (0) +#define OUTPUT_STD_EIA0_2 (0x00) +#define OUTPUT_STD_EIA0_1 (0x01) +#define OUTPUT_STD_FULL (0x02) +#define EMBEDDED_SYNC (0x04) +#define EXTERNAL_SYNC (0xFB) +#define STD_MODE_SHIFT (3) +#define STD_MODE_MASK (0x1F) +#define STD_MODE_720P (0x05) +#define STD_MODE_720P_25 (0x08) +#define STD_MODE_720P_30 (0x07) +#define STD_MODE_720P_50 (0x06) +#define STD_MODE_1080I (0x0D) +#define STD_MODE_1080I_25fps (0x0E) +#define STD_MODE_1080P_24 (0x12) +#define STD_MODE_1080P_25 (0x10) +#define STD_MODE_1080P_30 (0x0F) +#define STD_MODE_525P (0x00) +#define STD_MODE_625P (0x03) + +/* Bit masks for SD Mode Register 1 */ +#define SD_STD_MASK (0x03) +#define SD_STD_NTSC (0x00) +#define SD_STD_PAL_BDGHI (0x01) +#define SD_STD_PAL_M (0x02) +#define SD_STD_PAL_N (0x03) +#define SD_LUMA_FLTR_MASK (0x7) +#define SD_LUMA_FLTR_SHIFT (0x2) +#define SD_CHROMA_FLTR_MASK (0x7) +#define SD_CHROMA_FLTR_SHIFT (0x5) + +/* Bit masks for SD Mode Register 2 */ +#define SD_PBPR_SSAF_EN (0x01) +#define SD_PBPR_SSAF_DI (0xFE) +#define SD_DAC_1_DI (0xFD) +#define SD_DAC_2_DI (0xFB) +#define SD_PEDESTAL_EN (0x08) +#define SD_PEDESTAL_DI (0xF7) +#define SD_SQUARE_PIXEL_EN (0x10) +#define SD_SQUARE_PIXEL_DI (0xEF) +#define SD_PIXEL_DATA_VALID (0x40) +#define SD_ACTIVE_EDGE_EN (0x80) +#define SD_ACTIVE_EDGE_DI (0x7F) + +/* Bit masks for HD Mode Register 6 */ +#define HD_RGB_INPUT_EN (0x02) +#define HD_RGB_INPUT_DI (0xFD) +#define HD_PBPR_SYNC_EN (0x04) +#define HD_PBPR_SYNC_DI (0xFB) +#define HD_DAC_SWAP_EN (0x08) +#define HD_DAC_SWAP_DI (0xF7) +#define HD_GAMMA_CURVE_A (0xEF) +#define HD_GAMMA_CURVE_B (0x10) +#define HD_GAMMA_EN (0x20) +#define HD_GAMMA_DI (0xDF) +#define HD_ADPT_FLTR_MODEB (0x40) +#define HD_ADPT_FLTR_MODEA (0xBF) +#define HD_ADPT_FLTR_EN (0x80) +#define HD_ADPT_FLTR_DI (0x7F) + +#define ADV7343_BRIGHTNESS_MAX (127) +#define ADV7343_BRIGHTNESS_MIN (0) +#define ADV7343_BRIGHTNESS_DEF (3) +#define ADV7343_HUE_MAX (255) +#define ADV7343_HUE_MIN (0) +#define ADV7343_HUE_DEF (127) +#define ADV7343_GAIN_MAX (64) +#define ADV7343_GAIN_MIN (-64) +#define ADV7343_GAIN_DEF (0) + +#endif diff --git a/drivers/media/i2c/adv7393.c b/drivers/media/i2c/adv7393.c new file mode 100644 index 00000000000..3dc6098c726 --- /dev/null +++ b/drivers/media/i2c/adv7393.c @@ -0,0 +1,487 @@ +/* + * adv7393 - ADV7393 Video Encoder Driver + * + * The encoder hardware does not support SECAM. + * + * Copyright (C) 2010-2012 ADVANSEE - http://www.advansee.com/ + * Benoît Thébaudeau + * + * Based on ADV7343 driver, + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "adv7393_regs.h" + +MODULE_DESCRIPTION("ADV7393 video encoder driver"); +MODULE_LICENSE("GPL"); + +static bool debug; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Debug level 0-1"); + +struct adv7393_state { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + u8 reg00; + u8 reg01; + u8 reg02; + u8 reg35; + u8 reg80; + u8 reg82; + u32 output; + v4l2_std_id std; +}; + +static inline struct adv7393_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct adv7393_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct adv7393_state, hdl)->sd; +} + +static inline int adv7393_write(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_write_byte_data(client, reg, value); +} + +static const u8 adv7393_init_reg_val[] = { + ADV7393_SOFT_RESET, ADV7393_SOFT_RESET_DEFAULT, + ADV7393_POWER_MODE_REG, ADV7393_POWER_MODE_REG_DEFAULT, + + ADV7393_HD_MODE_REG1, ADV7393_HD_MODE_REG1_DEFAULT, + ADV7393_HD_MODE_REG2, ADV7393_HD_MODE_REG2_DEFAULT, + ADV7393_HD_MODE_REG3, ADV7393_HD_MODE_REG3_DEFAULT, + ADV7393_HD_MODE_REG4, ADV7393_HD_MODE_REG4_DEFAULT, + ADV7393_HD_MODE_REG5, ADV7393_HD_MODE_REG5_DEFAULT, + ADV7393_HD_MODE_REG6, ADV7393_HD_MODE_REG6_DEFAULT, + ADV7393_HD_MODE_REG7, ADV7393_HD_MODE_REG7_DEFAULT, + + ADV7393_SD_MODE_REG1, ADV7393_SD_MODE_REG1_DEFAULT, + ADV7393_SD_MODE_REG2, ADV7393_SD_MODE_REG2_DEFAULT, + ADV7393_SD_MODE_REG3, ADV7393_SD_MODE_REG3_DEFAULT, + ADV7393_SD_MODE_REG4, ADV7393_SD_MODE_REG4_DEFAULT, + ADV7393_SD_MODE_REG5, ADV7393_SD_MODE_REG5_DEFAULT, + ADV7393_SD_MODE_REG6, ADV7393_SD_MODE_REG6_DEFAULT, + ADV7393_SD_MODE_REG7, ADV7393_SD_MODE_REG7_DEFAULT, + ADV7393_SD_MODE_REG8, ADV7393_SD_MODE_REG8_DEFAULT, + + ADV7393_SD_TIMING_REG0, ADV7393_SD_TIMING_REG0_DEFAULT, + + ADV7393_SD_HUE_ADJUST, ADV7393_SD_HUE_ADJUST_DEFAULT, + ADV7393_SD_CGMS_WSS0, ADV7393_SD_CGMS_WSS0_DEFAULT, + ADV7393_SD_BRIGHTNESS_WSS, ADV7393_SD_BRIGHTNESS_WSS_DEFAULT, +}; + +/* + * 2^32 + * FSC(reg) = FSC (HZ) * -------- + * 27000000 + */ +static const struct adv7393_std_info stdinfo[] = { + { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_NTSC, 705268427, V4L2_STD_NTSC_443, + }, { + /* FSC(Hz) = 3,579,545.45 Hz */ + SD_STD_NTSC, 569408542, V4L2_STD_NTSC, + }, { + /* FSC(Hz) = 3,575,611.00 Hz */ + SD_STD_PAL_M, 568782678, V4L2_STD_PAL_M, + }, { + /* FSC(Hz) = 3,582,056.00 Hz */ + SD_STD_PAL_N, 569807903, V4L2_STD_PAL_Nc, + }, { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_PAL_N, 705268427, V4L2_STD_PAL_N, + }, { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_PAL_M, 705268427, V4L2_STD_PAL_60, + }, { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_PAL_BDGHI, 705268427, V4L2_STD_PAL, + }, +}; + +static int adv7393_setstd(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7393_state *state = to_state(sd); + const struct adv7393_std_info *std_info; + int num_std; + u8 reg; + u32 val; + int err = 0; + int i; + + num_std = ARRAY_SIZE(stdinfo); + + for (i = 0; i < num_std; i++) { + if (stdinfo[i].stdid & std) + break; + } + + if (i == num_std) { + v4l2_dbg(1, debug, sd, + "Invalid std or std is not supported: %llx\n", + (unsigned long long)std); + return -EINVAL; + } + + std_info = &stdinfo[i]; + + /* Set the standard */ + val = state->reg80 & ~SD_STD_MASK; + val |= std_info->standard_val3; + err = adv7393_write(sd, ADV7393_SD_MODE_REG1, val); + if (err < 0) + goto setstd_exit; + + state->reg80 = val; + + /* Configure the input mode register */ + val = state->reg01 & ~INPUT_MODE_MASK; + val |= SD_INPUT_MODE; + err = adv7393_write(sd, ADV7393_MODE_SELECT_REG, val); + if (err < 0) + goto setstd_exit; + + state->reg01 = val; + + /* Program the sub carrier frequency registers */ + val = std_info->fsc_val; + for (reg = ADV7393_FSC_REG0; reg <= ADV7393_FSC_REG3; reg++) { + err = adv7393_write(sd, reg, val); + if (err < 0) + goto setstd_exit; + val >>= 8; + } + + val = state->reg82; + + /* Pedestal settings */ + if (std & (V4L2_STD_NTSC | V4L2_STD_NTSC_443)) + val |= SD_PEDESTAL_EN; + else + val &= SD_PEDESTAL_DI; + + err = adv7393_write(sd, ADV7393_SD_MODE_REG2, val); + if (err < 0) + goto setstd_exit; + + state->reg82 = val; + +setstd_exit: + if (err != 0) + v4l2_err(sd, "Error setting std, write failed\n"); + + return err; +} + +static int adv7393_setoutput(struct v4l2_subdev *sd, u32 output_type) +{ + struct adv7393_state *state = to_state(sd); + u8 val; + int err = 0; + + if (output_type > ADV7393_SVIDEO_ID) { + v4l2_dbg(1, debug, sd, + "Invalid output type or output type not supported:%d\n", + output_type); + return -EINVAL; + } + + /* Enable Appropriate DAC */ + val = state->reg00 & 0x03; + + if (output_type == ADV7393_COMPOSITE_ID) + val |= ADV7393_COMPOSITE_POWER_VALUE; + else if (output_type == ADV7393_COMPONENT_ID) + val |= ADV7393_COMPONENT_POWER_VALUE; + else + val |= ADV7393_SVIDEO_POWER_VALUE; + + err = adv7393_write(sd, ADV7393_POWER_MODE_REG, val); + if (err < 0) + goto setoutput_exit; + + state->reg00 = val; + + /* Enable YUV output */ + val = state->reg02 | YUV_OUTPUT_SELECT; + err = adv7393_write(sd, ADV7393_MODE_REG0, val); + if (err < 0) + goto setoutput_exit; + + state->reg02 = val; + + /* configure SD DAC Output 1 bit */ + val = state->reg82; + if (output_type == ADV7393_COMPONENT_ID) + val &= SD_DAC_OUT1_DI; + else + val |= SD_DAC_OUT1_EN; + err = adv7393_write(sd, ADV7393_SD_MODE_REG2, val); + if (err < 0) + goto setoutput_exit; + + state->reg82 = val; + + /* configure ED/HD Color DAC Swap bit to zero */ + val = state->reg35 & HD_DAC_SWAP_DI; + err = adv7393_write(sd, ADV7393_HD_MODE_REG6, val); + if (err < 0) + goto setoutput_exit; + + state->reg35 = val; + +setoutput_exit: + if (err != 0) + v4l2_err(sd, "Error setting output, write failed\n"); + + return err; +} + +static int adv7393_log_status(struct v4l2_subdev *sd) +{ + struct adv7393_state *state = to_state(sd); + + v4l2_info(sd, "Standard: %llx\n", (unsigned long long)state->std); + v4l2_info(sd, "Output: %s\n", (state->output == 0) ? "Composite" : + ((state->output == 1) ? "Component" : "S-Video")); + return 0; +} + +static int adv7393_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + return adv7393_write(sd, ADV7393_SD_BRIGHTNESS_WSS, + ctrl->val & SD_BRIGHTNESS_VALUE_MASK); + + case V4L2_CID_HUE: + return adv7393_write(sd, ADV7393_SD_HUE_ADJUST, + ctrl->val - ADV7393_HUE_MIN); + + case V4L2_CID_GAIN: + return adv7393_write(sd, ADV7393_DAC123_OUTPUT_LEVEL, + ctrl->val); + } + return -EINVAL; +} + +static int adv7393_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7393, 0); +} + +static const struct v4l2_ctrl_ops adv7393_ctrl_ops = { + .s_ctrl = adv7393_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops adv7393_core_ops = { + .log_status = adv7393_log_status, + .g_chip_ident = adv7393_g_chip_ident, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, +}; + +static int adv7393_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7393_state *state = to_state(sd); + int err = 0; + + if (state->std == std) + return 0; + + err = adv7393_setstd(sd, std); + if (!err) + state->std = std; + + return err; +} + +static int adv7393_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct adv7393_state *state = to_state(sd); + int err = 0; + + if (state->output == output) + return 0; + + err = adv7393_setoutput(sd, output); + if (!err) + state->output = output; + + return err; +} + +static const struct v4l2_subdev_video_ops adv7393_video_ops = { + .s_std_output = adv7393_s_std_output, + .s_routing = adv7393_s_routing, +}; + +static const struct v4l2_subdev_ops adv7393_ops = { + .core = &adv7393_core_ops, + .video = &adv7393_video_ops, +}; + +static int adv7393_initialize(struct v4l2_subdev *sd) +{ + struct adv7393_state *state = to_state(sd); + int err = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(adv7393_init_reg_val); i += 2) { + + err = adv7393_write(sd, adv7393_init_reg_val[i], + adv7393_init_reg_val[i+1]); + if (err) { + v4l2_err(sd, "Error initializing\n"); + return err; + } + } + + /* Configure for default video standard */ + err = adv7393_setoutput(sd, state->output); + if (err < 0) { + v4l2_err(sd, "Error setting output during init\n"); + return -EINVAL; + } + + err = adv7393_setstd(sd, state->std); + if (err < 0) { + v4l2_err(sd, "Error setting std during init\n"); + return -EINVAL; + } + + return err; +} + +static int adv7393_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adv7393_state *state; + int err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct adv7393_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + + state->reg00 = ADV7393_POWER_MODE_REG_DEFAULT; + state->reg01 = 0x00; + state->reg02 = 0x20; + state->reg35 = ADV7393_HD_MODE_REG6_DEFAULT; + state->reg80 = ADV7393_SD_MODE_REG1_DEFAULT; + state->reg82 = ADV7393_SD_MODE_REG2_DEFAULT; + + state->output = ADV7393_COMPOSITE_ID; + state->std = V4L2_STD_NTSC; + + v4l2_i2c_subdev_init(&state->sd, client, &adv7393_ops); + + v4l2_ctrl_handler_init(&state->hdl, 3); + v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops, + V4L2_CID_BRIGHTNESS, ADV7393_BRIGHTNESS_MIN, + ADV7393_BRIGHTNESS_MAX, 1, + ADV7393_BRIGHTNESS_DEF); + v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops, + V4L2_CID_HUE, ADV7393_HUE_MIN, + ADV7393_HUE_MAX, 1, + ADV7393_HUE_DEF); + v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops, + V4L2_CID_GAIN, ADV7393_GAIN_MIN, + ADV7393_GAIN_MAX, 1, + ADV7393_GAIN_DEF); + state->sd.ctrl_handler = &state->hdl; + if (state->hdl.error) { + int err = state->hdl.error; + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + v4l2_ctrl_handler_setup(&state->hdl); + + err = adv7393_initialize(&state->sd); + if (err) { + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + } + return err; +} + +static int adv7393_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7393_state *state = to_state(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + + return 0; +} + +static const struct i2c_device_id adv7393_id[] = { + {"adv7393", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, adv7393_id); + +static struct i2c_driver adv7393_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "adv7393", + }, + .probe = adv7393_probe, + .remove = adv7393_remove, + .id_table = adv7393_id, +}; +module_i2c_driver(adv7393_driver); diff --git a/drivers/media/i2c/adv7393_regs.h b/drivers/media/i2c/adv7393_regs.h new file mode 100644 index 00000000000..78968330f0b --- /dev/null +++ b/drivers/media/i2c/adv7393_regs.h @@ -0,0 +1,188 @@ +/* + * ADV7393 encoder related structure and register definitions + * + * Copyright (C) 2010-2012 ADVANSEE - http://www.advansee.com/ + * Benoît Thébaudeau + * + * Based on ADV7343 driver, + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ADV7393_REGS_H +#define ADV7393_REGS_H + +struct adv7393_std_info { + u32 standard_val3; + u32 fsc_val; + v4l2_std_id stdid; +}; + +/* Register offset macros */ +#define ADV7393_POWER_MODE_REG (0x00) +#define ADV7393_MODE_SELECT_REG (0x01) +#define ADV7393_MODE_REG0 (0x02) + +#define ADV7393_DAC123_OUTPUT_LEVEL (0x0B) + +#define ADV7393_SOFT_RESET (0x17) + +#define ADV7393_HD_MODE_REG1 (0x30) +#define ADV7393_HD_MODE_REG2 (0x31) +#define ADV7393_HD_MODE_REG3 (0x32) +#define ADV7393_HD_MODE_REG4 (0x33) +#define ADV7393_HD_MODE_REG5 (0x34) +#define ADV7393_HD_MODE_REG6 (0x35) + +#define ADV7393_HD_MODE_REG7 (0x39) + +#define ADV7393_SD_MODE_REG1 (0x80) +#define ADV7393_SD_MODE_REG2 (0x82) +#define ADV7393_SD_MODE_REG3 (0x83) +#define ADV7393_SD_MODE_REG4 (0x84) +#define ADV7393_SD_MODE_REG5 (0x86) +#define ADV7393_SD_MODE_REG6 (0x87) +#define ADV7393_SD_MODE_REG7 (0x88) +#define ADV7393_SD_MODE_REG8 (0x89) + +#define ADV7393_SD_TIMING_REG0 (0x8A) + +#define ADV7393_FSC_REG0 (0x8C) +#define ADV7393_FSC_REG1 (0x8D) +#define ADV7393_FSC_REG2 (0x8E) +#define ADV7393_FSC_REG3 (0x8F) + +#define ADV7393_SD_CGMS_WSS0 (0x99) + +#define ADV7393_SD_HUE_ADJUST (0xA0) +#define ADV7393_SD_BRIGHTNESS_WSS (0xA1) + +/* Default values for the registers */ +#define ADV7393_POWER_MODE_REG_DEFAULT (0x10) +#define ADV7393_HD_MODE_REG1_DEFAULT (0x3C) /* Changed Default + 720p EAV/SAV code*/ +#define ADV7393_HD_MODE_REG2_DEFAULT (0x01) /* Changed Pixel data + valid */ +#define ADV7393_HD_MODE_REG3_DEFAULT (0x00) /* Color delay 0 clks */ +#define ADV7393_HD_MODE_REG4_DEFAULT (0xEC) /* Changed */ +#define ADV7393_HD_MODE_REG5_DEFAULT (0x08) +#define ADV7393_HD_MODE_REG6_DEFAULT (0x00) +#define ADV7393_HD_MODE_REG7_DEFAULT (0x00) +#define ADV7393_SOFT_RESET_DEFAULT (0x02) +#define ADV7393_COMPOSITE_POWER_VALUE (0x10) +#define ADV7393_COMPONENT_POWER_VALUE (0x1C) +#define ADV7393_SVIDEO_POWER_VALUE (0x0C) +#define ADV7393_SD_HUE_ADJUST_DEFAULT (0x80) +#define ADV7393_SD_BRIGHTNESS_WSS_DEFAULT (0x00) + +#define ADV7393_SD_CGMS_WSS0_DEFAULT (0x10) + +#define ADV7393_SD_MODE_REG1_DEFAULT (0x10) +#define ADV7393_SD_MODE_REG2_DEFAULT (0xC9) +#define ADV7393_SD_MODE_REG3_DEFAULT (0x00) +#define ADV7393_SD_MODE_REG4_DEFAULT (0x00) +#define ADV7393_SD_MODE_REG5_DEFAULT (0x02) +#define ADV7393_SD_MODE_REG6_DEFAULT (0x8C) +#define ADV7393_SD_MODE_REG7_DEFAULT (0x14) +#define ADV7393_SD_MODE_REG8_DEFAULT (0x00) + +#define ADV7393_SD_TIMING_REG0_DEFAULT (0x0C) + +/* Bit masks for Mode Select Register */ +#define INPUT_MODE_MASK (0x70) +#define SD_INPUT_MODE (0x00) +#define HD_720P_INPUT_MODE (0x10) +#define HD_1080I_INPUT_MODE (0x10) + +/* Bit masks for Mode Register 0 */ +#define TEST_PATTERN_BLACK_BAR_EN (0x04) +#define YUV_OUTPUT_SELECT (0x20) +#define RGB_OUTPUT_SELECT (0xDF) + +/* Bit masks for SD brightness/WSS */ +#define SD_BRIGHTNESS_VALUE_MASK (0x7F) +#define SD_BLANK_WSS_DATA_MASK (0x80) + +/* Bit masks for soft reset register */ +#define SOFT_RESET (0x02) + +/* Bit masks for HD Mode Register 1 */ +#define OUTPUT_STD_MASK (0x03) +#define OUTPUT_STD_SHIFT (0) +#define OUTPUT_STD_EIA0_2 (0x00) +#define OUTPUT_STD_EIA0_1 (0x01) +#define OUTPUT_STD_FULL (0x02) +#define EMBEDDED_SYNC (0x04) +#define EXTERNAL_SYNC (0xFB) +#define STD_MODE_MASK (0x1F) +#define STD_MODE_SHIFT (3) +#define STD_MODE_720P (0x05) +#define STD_MODE_720P_25 (0x08) +#define STD_MODE_720P_30 (0x07) +#define STD_MODE_720P_50 (0x06) +#define STD_MODE_1080I (0x0D) +#define STD_MODE_1080I_25 (0x0E) +#define STD_MODE_1080P_24 (0x11) +#define STD_MODE_1080P_25 (0x10) +#define STD_MODE_1080P_30 (0x0F) +#define STD_MODE_525P (0x00) +#define STD_MODE_625P (0x03) + +/* Bit masks for SD Mode Register 1 */ +#define SD_STD_MASK (0x03) +#define SD_STD_NTSC (0x00) +#define SD_STD_PAL_BDGHI (0x01) +#define SD_STD_PAL_M (0x02) +#define SD_STD_PAL_N (0x03) +#define SD_LUMA_FLTR_MASK (0x07) +#define SD_LUMA_FLTR_SHIFT (2) +#define SD_CHROMA_FLTR_MASK (0x07) +#define SD_CHROMA_FLTR_SHIFT (5) + +/* Bit masks for SD Mode Register 2 */ +#define SD_PRPB_SSAF_EN (0x01) +#define SD_PRPB_SSAF_DI (0xFE) +#define SD_DAC_OUT1_EN (0x02) +#define SD_DAC_OUT1_DI (0xFD) +#define SD_PEDESTAL_EN (0x08) +#define SD_PEDESTAL_DI (0xF7) +#define SD_SQUARE_PIXEL_EN (0x10) +#define SD_SQUARE_PIXEL_DI (0xEF) +#define SD_PIXEL_DATA_VALID (0x40) +#define SD_ACTIVE_EDGE_EN (0x80) +#define SD_ACTIVE_EDGE_DI (0x7F) + +/* Bit masks for HD Mode Register 6 */ +#define HD_PRPB_SYNC_EN (0x04) +#define HD_PRPB_SYNC_DI (0xFB) +#define HD_DAC_SWAP_EN (0x08) +#define HD_DAC_SWAP_DI (0xF7) +#define HD_GAMMA_CURVE_A (0xEF) +#define HD_GAMMA_CURVE_B (0x10) +#define HD_GAMMA_EN (0x20) +#define HD_GAMMA_DI (0xDF) +#define HD_ADPT_FLTR_MODEA (0xBF) +#define HD_ADPT_FLTR_MODEB (0x40) +#define HD_ADPT_FLTR_EN (0x80) +#define HD_ADPT_FLTR_DI (0x7F) + +#define ADV7393_BRIGHTNESS_MAX (63) +#define ADV7393_BRIGHTNESS_MIN (-64) +#define ADV7393_BRIGHTNESS_DEF (0) +#define ADV7393_HUE_MAX (127) +#define ADV7393_HUE_MIN (-128) +#define ADV7393_HUE_DEF (0) +#define ADV7393_GAIN_MAX (64) +#define ADV7393_GAIN_MIN (-64) +#define ADV7393_GAIN_DEF (0) + +#endif diff --git a/drivers/media/i2c/ak881x.c b/drivers/media/i2c/ak881x.c new file mode 100644 index 00000000000..ba674656b10 --- /dev/null +++ b/drivers/media/i2c/ak881x.c @@ -0,0 +1,359 @@ +/* + * Driver for AK8813 / AK8814 TV-ecoders from Asahi Kasei Microsystems Co., Ltd. (AKM) + * + * Copyright (C) 2010, Guennadi Liakhovetski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define AK881X_INTERFACE_MODE 0 +#define AK881X_VIDEO_PROCESS1 1 +#define AK881X_VIDEO_PROCESS2 2 +#define AK881X_VIDEO_PROCESS3 3 +#define AK881X_DAC_MODE 5 +#define AK881X_STATUS 0x24 +#define AK881X_DEVICE_ID 0x25 +#define AK881X_DEVICE_REVISION 0x26 + +struct ak881x { + struct v4l2_subdev subdev; + struct ak881x_pdata *pdata; + unsigned int lines; + int id; /* DEVICE_ID code V4L2_IDENT_AK881X code from v4l2-chip-ident.h */ + char revision; /* DEVICE_REVISION content */ +}; + +static int reg_read(struct i2c_client *client, const u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int reg_write(struct i2c_client *client, const u8 reg, + const u8 data) +{ + return i2c_smbus_write_byte_data(client, reg, data); +} + +static int reg_set(struct i2c_client *client, const u8 reg, + const u8 data, u8 mask) +{ + int ret = reg_read(client, reg); + if (ret < 0) + return ret; + return reg_write(client, reg, (ret & ~mask) | (data & mask)); +} + +static struct ak881x *to_ak881x(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct ak881x, subdev); +} + +static int ak881x_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ak881x *ak881x = to_ak881x(client); + + if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; + + if (id->match.addr != client->addr) + return -ENODEV; + + id->ident = ak881x->id; + id->revision = ak881x->revision; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ak881x_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x26) + return -EINVAL; + + if (reg->match.addr != client->addr) + return -ENODEV; + + reg->val = reg_read(client, reg->reg); + + if (reg->val > 0xffff) + return -EIO; + + return 0; +} + +static int ak881x_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x26) + return -EINVAL; + + if (reg->match.addr != client->addr) + return -ENODEV; + + if (reg_write(client, reg->reg, reg->val) < 0) + return -EIO; + + return 0; +} +#endif + +static int ak881x_try_g_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ak881x *ak881x = to_ak881x(client); + + v4l_bound_align_image(&mf->width, 0, 720, 2, + &mf->height, 0, ak881x->lines, 1, 0); + mf->field = V4L2_FIELD_INTERLACED; + mf->code = V4L2_MBUS_FMT_YUYV8_2X8; + mf->colorspace = V4L2_COLORSPACE_SMPTE170M; + + return 0; +} + +static int ak881x_s_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + if (mf->field != V4L2_FIELD_INTERLACED || + mf->code != V4L2_MBUS_FMT_YUYV8_2X8) + return -EINVAL; + + return ak881x_try_g_mbus_fmt(sd, mf); +} + +static int ak881x_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index, + enum v4l2_mbus_pixelcode *code) +{ + if (index) + return -EINVAL; + + *code = V4L2_MBUS_FMT_YUYV8_2X8; + return 0; +} + +static int ak881x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ak881x *ak881x = to_ak881x(client); + + a->bounds.left = 0; + a->bounds.top = 0; + a->bounds.width = 720; + a->bounds.height = ak881x->lines; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + +static int ak881x_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ak881x *ak881x = to_ak881x(client); + u8 vp1; + + if (std == V4L2_STD_NTSC_443) { + vp1 = 3; + ak881x->lines = 480; + } else if (std == V4L2_STD_PAL_M) { + vp1 = 5; + ak881x->lines = 480; + } else if (std == V4L2_STD_PAL_60) { + vp1 = 7; + ak881x->lines = 480; + } else if (std && !(std & ~V4L2_STD_PAL)) { + vp1 = 0xf; + ak881x->lines = 576; + } else if (std && !(std & ~V4L2_STD_NTSC)) { + vp1 = 0; + ak881x->lines = 480; + } else { + /* No SECAM or PAL_N/Nc supported */ + return -EINVAL; + } + + reg_set(client, AK881X_VIDEO_PROCESS1, vp1, 0xf); + + return 0; +} + +static int ak881x_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ak881x *ak881x = to_ak881x(client); + + if (enable) { + u8 dac; + /* For colour-bar testing set bit 6 of AK881X_VIDEO_PROCESS1 */ + /* Default: composite output */ + if (ak881x->pdata->flags & AK881X_COMPONENT) + dac = 3; + else + dac = 4; + /* Turn on the DAC(s) */ + reg_write(client, AK881X_DAC_MODE, dac); + dev_dbg(&client->dev, "chip status 0x%x\n", + reg_read(client, AK881X_STATUS)); + } else { + /* ...and clear bit 6 of AK881X_VIDEO_PROCESS1 here */ + reg_write(client, AK881X_DAC_MODE, 0); + dev_dbg(&client->dev, "chip status 0x%x\n", + reg_read(client, AK881X_STATUS)); + } + + return 0; +} + +static struct v4l2_subdev_core_ops ak881x_subdev_core_ops = { + .g_chip_ident = ak881x_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = ak881x_g_register, + .s_register = ak881x_s_register, +#endif +}; + +static struct v4l2_subdev_video_ops ak881x_subdev_video_ops = { + .s_mbus_fmt = ak881x_s_mbus_fmt, + .g_mbus_fmt = ak881x_try_g_mbus_fmt, + .try_mbus_fmt = ak881x_try_g_mbus_fmt, + .cropcap = ak881x_cropcap, + .enum_mbus_fmt = ak881x_enum_mbus_fmt, + .s_std_output = ak881x_s_std_output, + .s_stream = ak881x_s_stream, +}; + +static struct v4l2_subdev_ops ak881x_subdev_ops = { + .core = &ak881x_subdev_core_ops, + .video = &ak881x_subdev_video_ops, +}; + +static int ak881x_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct ak881x *ak881x; + u8 ifmode, data; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_warn(&adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + ak881x = kzalloc(sizeof(struct ak881x), GFP_KERNEL); + if (!ak881x) + return -ENOMEM; + + v4l2_i2c_subdev_init(&ak881x->subdev, client, &ak881x_subdev_ops); + + data = reg_read(client, AK881X_DEVICE_ID); + + switch (data) { + case 0x13: + ak881x->id = V4L2_IDENT_AK8813; + break; + case 0x14: + ak881x->id = V4L2_IDENT_AK8814; + break; + default: + dev_err(&client->dev, + "No ak881x chip detected, register read %x\n", data); + kfree(ak881x); + return -ENODEV; + } + + ak881x->revision = reg_read(client, AK881X_DEVICE_REVISION); + ak881x->pdata = client->dev.platform_data; + + if (ak881x->pdata) { + if (ak881x->pdata->flags & AK881X_FIELD) + ifmode = 4; + else + ifmode = 0; + + switch (ak881x->pdata->flags & AK881X_IF_MODE_MASK) { + case AK881X_IF_MODE_BT656: + ifmode |= 1; + break; + case AK881X_IF_MODE_MASTER: + ifmode |= 2; + break; + case AK881X_IF_MODE_SLAVE: + default: + break; + } + + dev_dbg(&client->dev, "IF mode %x\n", ifmode); + + /* + * "Line Blanking No." seems to be the same as the number of + * "black" lines on, e.g., SuperH VOU, whose default value of 20 + * "incidentally" matches ak881x' default + */ + reg_write(client, AK881X_INTERFACE_MODE, ifmode | (20 << 3)); + } + + /* Hardware default: NTSC-M */ + ak881x->lines = 480; + + dev_info(&client->dev, "Detected an ak881x chip ID %x, revision %x\n", + data, ak881x->revision); + + return 0; +} + +static int ak881x_remove(struct i2c_client *client) +{ + struct ak881x *ak881x = to_ak881x(client); + + v4l2_device_unregister_subdev(&ak881x->subdev); + kfree(ak881x); + + return 0; +} + +static const struct i2c_device_id ak881x_id[] = { + { "ak8813", 0 }, + { "ak8814", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ak881x_id); + +static struct i2c_driver ak881x_i2c_driver = { + .driver = { + .name = "ak881x", + }, + .probe = ak881x_probe, + .remove = ak881x_remove, + .id_table = ak881x_id, +}; + +module_i2c_driver(ak881x_i2c_driver); + +MODULE_DESCRIPTION("TV-output driver for ak8813/ak8814"); +MODULE_AUTHOR("Guennadi Liakhovetski "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/aptina-pll.c b/drivers/media/i2c/aptina-pll.c new file mode 100644 index 00000000000..8153a449846 --- /dev/null +++ b/drivers/media/i2c/aptina-pll.c @@ -0,0 +1,173 @@ +/* + * Aptina Sensor PLL Configuration + * + * Copyright (C) 2012 Laurent Pinchart + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include +#include +#include + +#include "aptina-pll.h" + +int aptina_pll_calculate(struct device *dev, + const struct aptina_pll_limits *limits, + struct aptina_pll *pll) +{ + unsigned int mf_min; + unsigned int mf_max; + unsigned int p1_min; + unsigned int p1_max; + unsigned int p1; + unsigned int div; + + dev_dbg(dev, "PLL: ext clock %u pix clock %u\n", + pll->ext_clock, pll->pix_clock); + + if (pll->ext_clock < limits->ext_clock_min || + pll->ext_clock > limits->ext_clock_max) { + dev_err(dev, "pll: invalid external clock frequency.\n"); + return -EINVAL; + } + + if (pll->pix_clock == 0 || pll->pix_clock > limits->pix_clock_max) { + dev_err(dev, "pll: invalid pixel clock frequency.\n"); + return -EINVAL; + } + + /* Compute the multiplier M and combined N*P1 divisor. */ + div = gcd(pll->pix_clock, pll->ext_clock); + pll->m = pll->pix_clock / div; + div = pll->ext_clock / div; + + /* We now have the smallest M and N*P1 values that will result in the + * desired pixel clock frequency, but they might be out of the valid + * range. Compute the factor by which we should multiply them given the + * following constraints: + * + * - minimum/maximum multiplier + * - minimum/maximum multiplier output clock frequency assuming the + * minimum/maximum N value + * - minimum/maximum combined N*P1 divisor + */ + mf_min = DIV_ROUND_UP(limits->m_min, pll->m); + mf_min = max(mf_min, limits->out_clock_min / + (pll->ext_clock / limits->n_min * pll->m)); + mf_min = max(mf_min, limits->n_min * limits->p1_min / div); + mf_max = limits->m_max / pll->m; + mf_max = min(mf_max, limits->out_clock_max / + (pll->ext_clock / limits->n_max * pll->m)); + mf_max = min(mf_max, DIV_ROUND_UP(limits->n_max * limits->p1_max, div)); + + dev_dbg(dev, "pll: mf min %u max %u\n", mf_min, mf_max); + if (mf_min > mf_max) { + dev_err(dev, "pll: no valid combined N*P1 divisor.\n"); + return -EINVAL; + } + + /* + * We're looking for the highest acceptable P1 value for which a + * multiplier factor MF exists that fulfills the following conditions: + * + * 1. p1 is in the [p1_min, p1_max] range given by the limits and is + * even + * 2. mf is in the [mf_min, mf_max] range computed above + * 3. div * mf is a multiple of p1, in order to compute + * n = div * mf / p1 + * m = pll->m * mf + * 4. the internal clock frequency, given by ext_clock / n, is in the + * [int_clock_min, int_clock_max] range given by the limits + * 5. the output clock frequency, given by ext_clock / n * m, is in the + * [out_clock_min, out_clock_max] range given by the limits + * + * The first naive approach is to iterate over all p1 values acceptable + * according to (1) and all mf values acceptable according to (2), and + * stop at the first combination that fulfills (3), (4) and (5). This + * has a O(n^2) complexity. + * + * Instead of iterating over all mf values in the [mf_min, mf_max] range + * we can compute the mf increment between two acceptable values + * according to (3) with + * + * mf_inc = p1 / gcd(div, p1) (6) + * + * and round the minimum up to the nearest multiple of mf_inc. This will + * restrict the number of mf values to be checked. + * + * Furthermore, conditions (4) and (5) only restrict the range of + * acceptable p1 and mf values by modifying the minimum and maximum + * limits. (5) can be expressed as + * + * ext_clock / (div * mf / p1) * m * mf >= out_clock_min + * ext_clock / (div * mf / p1) * m * mf <= out_clock_max + * + * or + * + * p1 >= out_clock_min * div / (ext_clock * m) (7) + * p1 <= out_clock_max * div / (ext_clock * m) + * + * Similarly, (4) can be expressed as + * + * mf >= ext_clock * p1 / (int_clock_max * div) (8) + * mf <= ext_clock * p1 / (int_clock_min * div) + * + * We can thus iterate over the restricted p1 range defined by the + * combination of (1) and (7), and then compute the restricted mf range + * defined by the combination of (2), (6) and (8). If the resulting mf + * range is not empty, any value in the mf range is acceptable. We thus + * select the mf lwoer bound and the corresponding p1 value. + */ + if (limits->p1_min == 0) { + dev_err(dev, "pll: P1 minimum value must be >0.\n"); + return -EINVAL; + } + + p1_min = max(limits->p1_min, DIV_ROUND_UP(limits->out_clock_min * div, + pll->ext_clock * pll->m)); + p1_max = min(limits->p1_max, limits->out_clock_max * div / + (pll->ext_clock * pll->m)); + + for (p1 = p1_max & ~1; p1 >= p1_min; p1 -= 2) { + unsigned int mf_inc = p1 / gcd(div, p1); + unsigned int mf_high; + unsigned int mf_low; + + mf_low = roundup(max(mf_min, DIV_ROUND_UP(pll->ext_clock * p1, + limits->int_clock_max * div)), mf_inc); + mf_high = min(mf_max, pll->ext_clock * p1 / + (limits->int_clock_min * div)); + + if (mf_low > mf_high) + continue; + + pll->n = div * mf_low / p1; + pll->m *= mf_low; + pll->p1 = p1; + dev_dbg(dev, "PLL: N %u M %u P1 %u\n", pll->n, pll->m, pll->p1); + return 0; + } + + dev_err(dev, "pll: no valid N and P1 divisors found.\n"); + return -EINVAL; +} +EXPORT_SYMBOL_GPL(aptina_pll_calculate); + +MODULE_DESCRIPTION("Aptina PLL Helpers"); +MODULE_AUTHOR("Laurent Pinchart "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/aptina-pll.h b/drivers/media/i2c/aptina-pll.h new file mode 100644 index 00000000000..b370e341e75 --- /dev/null +++ b/drivers/media/i2c/aptina-pll.h @@ -0,0 +1,56 @@ +/* + * Aptina Sensor PLL Configuration + * + * Copyright (C) 2012 Laurent Pinchart + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef __APTINA_PLL_H +#define __APTINA_PLL_H + +struct aptina_pll { + unsigned int ext_clock; + unsigned int pix_clock; + + unsigned int n; + unsigned int m; + unsigned int p1; +}; + +struct aptina_pll_limits { + unsigned int ext_clock_min; + unsigned int ext_clock_max; + unsigned int int_clock_min; + unsigned int int_clock_max; + unsigned int out_clock_min; + unsigned int out_clock_max; + unsigned int pix_clock_max; + + unsigned int n_min; + unsigned int n_max; + unsigned int m_min; + unsigned int m_max; + unsigned int p1_min; + unsigned int p1_max; +}; + +struct device; + +int aptina_pll_calculate(struct device *dev, + const struct aptina_pll_limits *limits, + struct aptina_pll *pll); + +#endif /* __APTINA_PLL_H */ diff --git a/drivers/media/i2c/as3645a.c b/drivers/media/i2c/as3645a.c new file mode 100644 index 00000000000..3bfdbf9d9bf --- /dev/null +++ b/drivers/media/i2c/as3645a.c @@ -0,0 +1,888 @@ +/* + * drivers/media/i2c/as3645a.c - AS3645A and LM3555 flash controllers driver + * + * Copyright (C) 2008-2011 Nokia Corporation + * Copyright (c) 2011, Intel Corporation. + * + * Contact: Laurent Pinchart + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * TODO: + * - Check hardware FSTROBE control when sensor driver add support for this + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define AS_TIMER_MS_TO_CODE(t) (((t) - 100) / 50) +#define AS_TIMER_CODE_TO_MS(c) (50 * (c) + 100) + +/* Register definitions */ + +/* Read-only Design info register: Reset state: xxxx 0001 */ +#define AS_DESIGN_INFO_REG 0x00 +#define AS_DESIGN_INFO_FACTORY(x) (((x) >> 4)) +#define AS_DESIGN_INFO_MODEL(x) ((x) & 0x0f) + +/* Read-only Version control register: Reset state: 0000 0000 + * for first engineering samples + */ +#define AS_VERSION_CONTROL_REG 0x01 +#define AS_VERSION_CONTROL_RFU(x) (((x) >> 4)) +#define AS_VERSION_CONTROL_VERSION(x) ((x) & 0x0f) + +/* Read / Write (Indicator and timer register): Reset state: 0000 1111 */ +#define AS_INDICATOR_AND_TIMER_REG 0x02 +#define AS_INDICATOR_AND_TIMER_TIMEOUT_SHIFT 0 +#define AS_INDICATOR_AND_TIMER_VREF_SHIFT 4 +#define AS_INDICATOR_AND_TIMER_INDICATOR_SHIFT 6 + +/* Read / Write (Current set register): Reset state: 0110 1001 */ +#define AS_CURRENT_SET_REG 0x03 +#define AS_CURRENT_ASSIST_LIGHT_SHIFT 0 +#define AS_CURRENT_LED_DET_ON (1 << 3) +#define AS_CURRENT_FLASH_CURRENT_SHIFT 4 + +/* Read / Write (Control register): Reset state: 1011 0100 */ +#define AS_CONTROL_REG 0x04 +#define AS_CONTROL_MODE_SETTING_SHIFT 0 +#define AS_CONTROL_STROBE_ON (1 << 2) +#define AS_CONTROL_OUT_ON (1 << 3) +#define AS_CONTROL_EXT_TORCH_ON (1 << 4) +#define AS_CONTROL_STROBE_TYPE_EDGE (0 << 5) +#define AS_CONTROL_STROBE_TYPE_LEVEL (1 << 5) +#define AS_CONTROL_COIL_PEAK_SHIFT 6 + +/* Read only (D3 is read / write) (Fault and info): Reset state: 0000 x000 */ +#define AS_FAULT_INFO_REG 0x05 +#define AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT (1 << 1) +#define AS_FAULT_INFO_INDICATOR_LED (1 << 2) +#define AS_FAULT_INFO_LED_AMOUNT (1 << 3) +#define AS_FAULT_INFO_TIMEOUT (1 << 4) +#define AS_FAULT_INFO_OVER_TEMPERATURE (1 << 5) +#define AS_FAULT_INFO_SHORT_CIRCUIT (1 << 6) +#define AS_FAULT_INFO_OVER_VOLTAGE (1 << 7) + +/* Boost register */ +#define AS_BOOST_REG 0x0d +#define AS_BOOST_CURRENT_DISABLE (0 << 0) +#define AS_BOOST_CURRENT_ENABLE (1 << 0) + +/* Password register is used to unlock boost register writing */ +#define AS_PASSWORD_REG 0x0f +#define AS_PASSWORD_UNLOCK_VALUE 0x55 + +enum as_mode { + AS_MODE_EXT_TORCH = 0 << AS_CONTROL_MODE_SETTING_SHIFT, + AS_MODE_INDICATOR = 1 << AS_CONTROL_MODE_SETTING_SHIFT, + AS_MODE_ASSIST = 2 << AS_CONTROL_MODE_SETTING_SHIFT, + AS_MODE_FLASH = 3 << AS_CONTROL_MODE_SETTING_SHIFT, +}; + +/* + * struct as3645a + * + * @subdev: V4L2 subdev + * @pdata: Flash platform data + * @power_lock: Protects power_count + * @power_count: Power reference count + * @led_mode: V4L2 flash LED mode + * @timeout: Flash timeout in microseconds + * @flash_current: Flash current (0=200mA ... 15=500mA). Maximum + * values are 400mA for two LEDs and 500mA for one LED. + * @assist_current: Torch/Assist light current (0=20mA, 1=40mA ... 7=160mA) + * @indicator_current: Indicator LED current (0=0mA, 1=2.5mA ... 4=10mA) + * @strobe_source: Flash strobe source (software or external) + */ +struct as3645a { + struct v4l2_subdev subdev; + const struct as3645a_platform_data *pdata; + + struct mutex power_lock; + int power_count; + + /* Controls */ + struct v4l2_ctrl_handler ctrls; + + enum v4l2_flash_led_mode led_mode; + unsigned int timeout; + u8 flash_current; + u8 assist_current; + u8 indicator_current; + enum v4l2_flash_strobe_source strobe_source; +}; + +#define to_as3645a(sd) container_of(sd, struct as3645a, subdev) + +/* Return negative errno else zero on success */ +static int as3645a_write(struct as3645a *flash, u8 addr, u8 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); + int rval; + + rval = i2c_smbus_write_byte_data(client, addr, val); + + dev_dbg(&client->dev, "Write Addr:%02X Val:%02X %s\n", addr, val, + rval < 0 ? "fail" : "ok"); + + return rval; +} + +/* Return negative errno else a data byte received from the device. */ +static int as3645a_read(struct as3645a *flash, u8 addr) +{ + struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); + int rval; + + rval = i2c_smbus_read_byte_data(client, addr); + + dev_dbg(&client->dev, "Read Addr:%02X Val:%02X %s\n", addr, rval, + rval < 0 ? "fail" : "ok"); + + return rval; +} + +/* ----------------------------------------------------------------------------- + * Hardware configuration and trigger + */ + +/* + * as3645a_set_config - Set flash configuration registers + * @flash: The flash + * + * Configure the hardware with flash, assist and indicator currents, as well as + * flash timeout. + * + * Return 0 on success, or a negative error code if an I2C communication error + * occurred. + */ +static int as3645a_set_config(struct as3645a *flash) +{ + int ret; + u8 val; + + val = (flash->flash_current << AS_CURRENT_FLASH_CURRENT_SHIFT) + | (flash->assist_current << AS_CURRENT_ASSIST_LIGHT_SHIFT) + | AS_CURRENT_LED_DET_ON; + + ret = as3645a_write(flash, AS_CURRENT_SET_REG, val); + if (ret < 0) + return ret; + + val = AS_TIMER_MS_TO_CODE(flash->timeout / 1000) + << AS_INDICATOR_AND_TIMER_TIMEOUT_SHIFT; + + val |= (flash->pdata->vref << AS_INDICATOR_AND_TIMER_VREF_SHIFT) + | ((flash->indicator_current ? flash->indicator_current - 1 : 0) + << AS_INDICATOR_AND_TIMER_INDICATOR_SHIFT); + + return as3645a_write(flash, AS_INDICATOR_AND_TIMER_REG, val); +} + +/* + * as3645a_set_control - Set flash control register + * @flash: The flash + * @mode: Desired output mode + * @on: Desired output state + * + * Configure the hardware with output mode and state. + * + * Return 0 on success, or a negative error code if an I2C communication error + * occurred. + */ +static int +as3645a_set_control(struct as3645a *flash, enum as_mode mode, bool on) +{ + u8 reg; + + /* Configure output parameters and operation mode. */ + reg = (flash->pdata->peak << AS_CONTROL_COIL_PEAK_SHIFT) + | (on ? AS_CONTROL_OUT_ON : 0) + | mode; + + if (flash->led_mode == V4L2_FLASH_LED_MODE_FLASH && + flash->strobe_source == V4L2_FLASH_STROBE_SOURCE_EXTERNAL) { + reg |= AS_CONTROL_STROBE_TYPE_LEVEL + | AS_CONTROL_STROBE_ON; + } + + return as3645a_write(flash, AS_CONTROL_REG, reg); +} + +/* + * as3645a_set_output - Configure output and operation mode + * @flash: Flash controller + * @strobe: Strobe the flash (only valid in flash mode) + * + * Turn the LEDs output on/off and set the operation mode based on the current + * parameters. + * + * The AS3645A can't control the indicator LED independently of the flash/torch + * LED. If the flash controller is in V4L2_FLASH_LED_MODE_NONE mode, set the + * chip to indicator mode. Otherwise set it to assist light (torch) or flash + * mode. + * + * In indicator and assist modes, turn the output on/off based on the indicator + * and torch currents. In software strobe flash mode, turn the output on/off + * based on the strobe parameter. + */ +static int as3645a_set_output(struct as3645a *flash, bool strobe) +{ + enum as_mode mode; + bool on; + + switch (flash->led_mode) { + case V4L2_FLASH_LED_MODE_NONE: + on = flash->indicator_current != 0; + mode = AS_MODE_INDICATOR; + break; + case V4L2_FLASH_LED_MODE_TORCH: + on = true; + mode = AS_MODE_ASSIST; + break; + case V4L2_FLASH_LED_MODE_FLASH: + on = strobe; + mode = AS_MODE_FLASH; + break; + default: + BUG(); + } + + /* Configure output parameters and operation mode. */ + return as3645a_set_control(flash, mode, on); +} + +/* ----------------------------------------------------------------------------- + * V4L2 controls + */ + +static int as3645a_is_active(struct as3645a *flash) +{ + int ret; + + ret = as3645a_read(flash, AS_CONTROL_REG); + return ret < 0 ? ret : !!(ret & AS_CONTROL_OUT_ON); +} + +static int as3645a_read_fault(struct as3645a *flash) +{ + struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); + int rval; + + /* NOTE: reading register clear fault status */ + rval = as3645a_read(flash, AS_FAULT_INFO_REG); + if (rval < 0) + return rval; + + if (rval & AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT) + dev_dbg(&client->dev, "Inductor Peak limit fault\n"); + + if (rval & AS_FAULT_INFO_INDICATOR_LED) + dev_dbg(&client->dev, "Indicator LED fault: " + "Short circuit or open loop\n"); + + dev_dbg(&client->dev, "%u connected LEDs\n", + rval & AS_FAULT_INFO_LED_AMOUNT ? 2 : 1); + + if (rval & AS_FAULT_INFO_TIMEOUT) + dev_dbg(&client->dev, "Timeout fault\n"); + + if (rval & AS_FAULT_INFO_OVER_TEMPERATURE) + dev_dbg(&client->dev, "Over temperature fault\n"); + + if (rval & AS_FAULT_INFO_SHORT_CIRCUIT) + dev_dbg(&client->dev, "Short circuit fault\n"); + + if (rval & AS_FAULT_INFO_OVER_VOLTAGE) + dev_dbg(&client->dev, "Over voltage fault: " + "Indicates missing capacitor or open connection\n"); + + return rval; +} + +static int as3645a_get_ctrl(struct v4l2_ctrl *ctrl) +{ + struct as3645a *flash = + container_of(ctrl->handler, struct as3645a, ctrls); + struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); + int value; + + switch (ctrl->id) { + case V4L2_CID_FLASH_FAULT: + value = as3645a_read_fault(flash); + if (value < 0) + return value; + + ctrl->cur.val = 0; + if (value & AS_FAULT_INFO_SHORT_CIRCUIT) + ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT; + if (value & AS_FAULT_INFO_OVER_TEMPERATURE) + ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE; + if (value & AS_FAULT_INFO_TIMEOUT) + ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT; + if (value & AS_FAULT_INFO_OVER_VOLTAGE) + ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE; + if (value & AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT) + ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_CURRENT; + if (value & AS_FAULT_INFO_INDICATOR_LED) + ctrl->cur.val |= V4L2_FLASH_FAULT_INDICATOR; + break; + + case V4L2_CID_FLASH_STROBE_STATUS: + if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) { + ctrl->cur.val = 0; + break; + } + + value = as3645a_is_active(flash); + if (value < 0) + return value; + + ctrl->cur.val = value; + break; + } + + dev_dbg(&client->dev, "G_CTRL %08x:%d\n", ctrl->id, ctrl->cur.val); + + return 0; +} + +static int as3645a_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct as3645a *flash = + container_of(ctrl->handler, struct as3645a, ctrls); + struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); + int ret; + + dev_dbg(&client->dev, "S_CTRL %08x:%d\n", ctrl->id, ctrl->val); + + /* If a control that doesn't apply to the current mode is modified, + * we store the value and return immediately. The setting will be + * applied when the LED mode is changed. Otherwise we apply the setting + * immediately. + */ + + switch (ctrl->id) { + case V4L2_CID_FLASH_LED_MODE: + if (flash->indicator_current) + return -EBUSY; + + ret = as3645a_set_config(flash); + if (ret < 0) + return ret; + + flash->led_mode = ctrl->val; + return as3645a_set_output(flash, false); + + case V4L2_CID_FLASH_STROBE_SOURCE: + flash->strobe_source = ctrl->val; + + /* Applies to flash mode only. */ + if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) + break; + + return as3645a_set_output(flash, false); + + case V4L2_CID_FLASH_STROBE: + if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) + return -EBUSY; + + return as3645a_set_output(flash, true); + + case V4L2_CID_FLASH_STROBE_STOP: + if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) + return -EBUSY; + + return as3645a_set_output(flash, false); + + case V4L2_CID_FLASH_TIMEOUT: + flash->timeout = ctrl->val; + + /* Applies to flash mode only. */ + if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) + break; + + return as3645a_set_config(flash); + + case V4L2_CID_FLASH_INTENSITY: + flash->flash_current = (ctrl->val - AS3645A_FLASH_INTENSITY_MIN) + / AS3645A_FLASH_INTENSITY_STEP; + + /* Applies to flash mode only. */ + if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) + break; + + return as3645a_set_config(flash); + + case V4L2_CID_FLASH_TORCH_INTENSITY: + flash->assist_current = + (ctrl->val - AS3645A_TORCH_INTENSITY_MIN) + / AS3645A_TORCH_INTENSITY_STEP; + + /* Applies to torch mode only. */ + if (flash->led_mode != V4L2_FLASH_LED_MODE_TORCH) + break; + + return as3645a_set_config(flash); + + case V4L2_CID_FLASH_INDICATOR_INTENSITY: + if (flash->led_mode != V4L2_FLASH_LED_MODE_NONE) + return -EBUSY; + + flash->indicator_current = + (ctrl->val - AS3645A_INDICATOR_INTENSITY_MIN) + / AS3645A_INDICATOR_INTENSITY_STEP; + + ret = as3645a_set_config(flash); + if (ret < 0) + return ret; + + if ((ctrl->val == 0) == (ctrl->cur.val == 0)) + break; + + return as3645a_set_output(flash, false); + } + + return 0; +} + +static const struct v4l2_ctrl_ops as3645a_ctrl_ops = { + .g_volatile_ctrl = as3645a_get_ctrl, + .s_ctrl = as3645a_set_ctrl, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 subdev core operations + */ + +/* Put device into know state. */ +static int as3645a_setup(struct as3645a *flash) +{ + struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); + int ret; + + /* clear errors */ + ret = as3645a_read(flash, AS_FAULT_INFO_REG); + if (ret < 0) + return ret; + + dev_dbg(&client->dev, "Fault info: %02x\n", ret); + + ret = as3645a_set_config(flash); + if (ret < 0) + return ret; + + ret = as3645a_set_output(flash, false); + if (ret < 0) + return ret; + + /* read status */ + ret = as3645a_read_fault(flash); + if (ret < 0) + return ret; + + dev_dbg(&client->dev, "AS_INDICATOR_AND_TIMER_REG: %02x\n", + as3645a_read(flash, AS_INDICATOR_AND_TIMER_REG)); + dev_dbg(&client->dev, "AS_CURRENT_SET_REG: %02x\n", + as3645a_read(flash, AS_CURRENT_SET_REG)); + dev_dbg(&client->dev, "AS_CONTROL_REG: %02x\n", + as3645a_read(flash, AS_CONTROL_REG)); + + return ret & ~AS_FAULT_INFO_LED_AMOUNT ? -EIO : 0; +} + +static int __as3645a_set_power(struct as3645a *flash, int on) +{ + int ret; + + if (!on) + as3645a_set_control(flash, AS_MODE_EXT_TORCH, false); + + if (flash->pdata->set_power) { + ret = flash->pdata->set_power(&flash->subdev, on); + if (ret < 0) + return ret; + } + + if (!on) + return 0; + + ret = as3645a_setup(flash); + if (ret < 0) { + if (flash->pdata->set_power) + flash->pdata->set_power(&flash->subdev, 0); + } + + return ret; +} + +static int as3645a_set_power(struct v4l2_subdev *sd, int on) +{ + struct as3645a *flash = to_as3645a(sd); + int ret = 0; + + mutex_lock(&flash->power_lock); + + if (flash->power_count == !on) { + ret = __as3645a_set_power(flash, !!on); + if (ret < 0) + goto done; + } + + flash->power_count += on ? 1 : -1; + WARN_ON(flash->power_count < 0); + +done: + mutex_unlock(&flash->power_lock); + return ret; +} + +static int as3645a_registered(struct v4l2_subdev *sd) +{ + struct as3645a *flash = to_as3645a(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int rval, man, model, rfu, version; + const char *vendor; + + /* Power up the flash driver and read manufacturer ID, model ID, RFU + * and version. + */ + rval = as3645a_set_power(&flash->subdev, 1); + if (rval < 0) + return rval; + + rval = as3645a_read(flash, AS_DESIGN_INFO_REG); + if (rval < 0) + goto power_off; + + man = AS_DESIGN_INFO_FACTORY(rval); + model = AS_DESIGN_INFO_MODEL(rval); + + rval = as3645a_read(flash, AS_VERSION_CONTROL_REG); + if (rval < 0) + goto power_off; + + rfu = AS_VERSION_CONTROL_RFU(rval); + version = AS_VERSION_CONTROL_VERSION(rval); + + /* Verify the chip model and version. */ + if (model != 0x01 || rfu != 0x00) { + dev_err(&client->dev, "AS3645A not detected " + "(model %d rfu %d)\n", model, rfu); + rval = -ENODEV; + goto power_off; + } + + switch (man) { + case 1: + vendor = "AMS, Austria Micro Systems"; + break; + case 2: + vendor = "ADI, Analog Devices Inc."; + break; + case 3: + vendor = "NSC, National Semiconductor"; + break; + case 4: + vendor = "NXP"; + break; + case 5: + vendor = "TI, Texas Instrument"; + break; + default: + vendor = "Unknown"; + } + + dev_info(&client->dev, "Chip vendor: %s (%d) Version: %d\n", vendor, + man, version); + + rval = as3645a_write(flash, AS_PASSWORD_REG, AS_PASSWORD_UNLOCK_VALUE); + if (rval < 0) + goto power_off; + + rval = as3645a_write(flash, AS_BOOST_REG, AS_BOOST_CURRENT_DISABLE); + if (rval < 0) + goto power_off; + + /* Setup default values. This makes sure that the chip is in a known + * state, in case the power rail can't be controlled. + */ + rval = as3645a_setup(flash); + +power_off: + as3645a_set_power(&flash->subdev, 0); + + return rval; +} + +static int as3645a_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + return as3645a_set_power(sd, 1); +} + +static int as3645a_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + return as3645a_set_power(sd, 0); +} + +static const struct v4l2_subdev_core_ops as3645a_core_ops = { + .s_power = as3645a_set_power, +}; + +static const struct v4l2_subdev_ops as3645a_ops = { + .core = &as3645a_core_ops, +}; + +static const struct v4l2_subdev_internal_ops as3645a_internal_ops = { + .registered = as3645a_registered, + .open = as3645a_open, + .close = as3645a_close, +}; + +/* ----------------------------------------------------------------------------- + * I2C driver + */ +#ifdef CONFIG_PM + +static int as3645a_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct as3645a *flash = to_as3645a(subdev); + int rval; + + if (flash->power_count == 0) + return 0; + + rval = __as3645a_set_power(flash, 0); + + dev_dbg(&client->dev, "Suspend %s\n", rval < 0 ? "failed" : "ok"); + + return rval; +} + +static int as3645a_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct as3645a *flash = to_as3645a(subdev); + int rval; + + if (flash->power_count == 0) + return 0; + + rval = __as3645a_set_power(flash, 1); + + dev_dbg(&client->dev, "Resume %s\n", rval < 0 ? "fail" : "ok"); + + return rval; +} + +#else + +#define as3645a_suspend NULL +#define as3645a_resume NULL + +#endif /* CONFIG_PM */ + +/* + * as3645a_init_controls - Create controls + * @flash: The flash + * + * The number of LEDs reported in platform data is used to compute default + * limits. Parameters passed through platform data can override those limits. + */ +static int __devinit as3645a_init_controls(struct as3645a *flash) +{ + const struct as3645a_platform_data *pdata = flash->pdata; + struct v4l2_ctrl *ctrl; + int maximum; + + v4l2_ctrl_handler_init(&flash->ctrls, 10); + + /* V4L2_CID_FLASH_LED_MODE */ + v4l2_ctrl_new_std_menu(&flash->ctrls, &as3645a_ctrl_ops, + V4L2_CID_FLASH_LED_MODE, 2, ~7, + V4L2_FLASH_LED_MODE_NONE); + + /* V4L2_CID_FLASH_STROBE_SOURCE */ + v4l2_ctrl_new_std_menu(&flash->ctrls, &as3645a_ctrl_ops, + V4L2_CID_FLASH_STROBE_SOURCE, + pdata->ext_strobe ? 1 : 0, + pdata->ext_strobe ? ~3 : ~1, + V4L2_FLASH_STROBE_SOURCE_SOFTWARE); + + flash->strobe_source = V4L2_FLASH_STROBE_SOURCE_SOFTWARE; + + /* V4L2_CID_FLASH_STROBE */ + v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, + V4L2_CID_FLASH_STROBE, 0, 0, 0, 0); + + /* V4L2_CID_FLASH_STROBE_STOP */ + v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, + V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0); + + /* V4L2_CID_FLASH_STROBE_STATUS */ + ctrl = v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, + V4L2_CID_FLASH_STROBE_STATUS, 0, 1, 1, 1); + if (ctrl != NULL) + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + + /* V4L2_CID_FLASH_TIMEOUT */ + maximum = pdata->timeout_max; + + v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, + V4L2_CID_FLASH_TIMEOUT, AS3645A_FLASH_TIMEOUT_MIN, + maximum, AS3645A_FLASH_TIMEOUT_STEP, maximum); + + flash->timeout = maximum; + + /* V4L2_CID_FLASH_INTENSITY */ + maximum = pdata->flash_max_current; + + v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, + V4L2_CID_FLASH_INTENSITY, AS3645A_FLASH_INTENSITY_MIN, + maximum, AS3645A_FLASH_INTENSITY_STEP, maximum); + + flash->flash_current = (maximum - AS3645A_FLASH_INTENSITY_MIN) + / AS3645A_FLASH_INTENSITY_STEP; + + /* V4L2_CID_FLASH_TORCH_INTENSITY */ + maximum = pdata->torch_max_current; + + v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, + V4L2_CID_FLASH_TORCH_INTENSITY, + AS3645A_TORCH_INTENSITY_MIN, maximum, + AS3645A_TORCH_INTENSITY_STEP, + AS3645A_TORCH_INTENSITY_MIN); + + flash->assist_current = 0; + + /* V4L2_CID_FLASH_INDICATOR_INTENSITY */ + v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, + V4L2_CID_FLASH_INDICATOR_INTENSITY, + AS3645A_INDICATOR_INTENSITY_MIN, + AS3645A_INDICATOR_INTENSITY_MAX, + AS3645A_INDICATOR_INTENSITY_STEP, + AS3645A_INDICATOR_INTENSITY_MIN); + + flash->indicator_current = 0; + + /* V4L2_CID_FLASH_FAULT */ + ctrl = v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, + V4L2_CID_FLASH_FAULT, 0, + V4L2_FLASH_FAULT_OVER_VOLTAGE | + V4L2_FLASH_FAULT_TIMEOUT | + V4L2_FLASH_FAULT_OVER_TEMPERATURE | + V4L2_FLASH_FAULT_SHORT_CIRCUIT, 0, 0); + if (ctrl != NULL) + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + + flash->subdev.ctrl_handler = &flash->ctrls; + + return flash->ctrls.error; +} + +static int __devinit as3645a_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + struct as3645a *flash; + int ret; + + if (client->dev.platform_data == NULL) + return -ENODEV; + + flash = kzalloc(sizeof(*flash), GFP_KERNEL); + if (flash == NULL) + return -ENOMEM; + + flash->pdata = client->dev.platform_data; + + v4l2_i2c_subdev_init(&flash->subdev, client, &as3645a_ops); + flash->subdev.internal_ops = &as3645a_internal_ops; + flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + ret = as3645a_init_controls(flash); + if (ret < 0) + goto done; + + ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0); + if (ret < 0) + goto done; + + flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; + + mutex_init(&flash->power_lock); + + flash->led_mode = V4L2_FLASH_LED_MODE_NONE; + +done: + if (ret < 0) { + v4l2_ctrl_handler_free(&flash->ctrls); + kfree(flash); + } + + return ret; +} + +static int __devexit as3645a_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct as3645a *flash = to_as3645a(subdev); + + v4l2_device_unregister_subdev(subdev); + v4l2_ctrl_handler_free(&flash->ctrls); + media_entity_cleanup(&flash->subdev.entity); + mutex_destroy(&flash->power_lock); + kfree(flash); + + return 0; +} + +static const struct i2c_device_id as3645a_id_table[] = { + { AS3645A_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, as3645a_id_table); + +static const struct dev_pm_ops as3645a_pm_ops = { + .suspend = as3645a_suspend, + .resume = as3645a_resume, +}; + +static struct i2c_driver as3645a_i2c_driver = { + .driver = { + .name = AS3645A_NAME, + .pm = &as3645a_pm_ops, + }, + .probe = as3645a_probe, + .remove = __devexit_p(as3645a_remove), + .id_table = as3645a_id_table, +}; + +module_i2c_driver(as3645a_i2c_driver); + +MODULE_AUTHOR("Laurent Pinchart "); +MODULE_DESCRIPTION("LED flash driver for AS3645A, LM3555 and their clones"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/bt819.c b/drivers/media/i2c/bt819.c new file mode 100644 index 00000000000..377bf05b1ef --- /dev/null +++ b/drivers/media/i2c/bt819.c @@ -0,0 +1,517 @@ +/* + * bt819 - BT819A VideoStream Decoder (Rockwell Part) + * + * Copyright (C) 1999 Mike Bernson + * Copyright (C) 1998 Dave Perks + * + * Modifications for LML33/DC10plus unified driver + * Copyright (C) 2000 Serguei Miridonov + * + * Changes by Ronald Bultje + * - moved over to linux>=2.4.x i2c protocol (9/9/2002) + * + * This code was modify/ported from the saa7111 driver written + * by Dave Perks. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("Brooktree-819 video decoder driver"); +MODULE_AUTHOR("Mike Bernson & Dave Perks"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + + +/* ----------------------------------------------------------------------- */ + +struct bt819 { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + unsigned char reg[32]; + + v4l2_std_id norm; + int ident; + int input; + int enable; +}; + +static inline struct bt819 *to_bt819(struct v4l2_subdev *sd) +{ + return container_of(sd, struct bt819, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct bt819, hdl)->sd; +} + +struct timing { + int hactive; + int hdelay; + int vactive; + int vdelay; + int hscale; + int vscale; +}; + +/* for values, see the bt819 datasheet */ +static struct timing timing_data[] = { + {864 - 24, 20, 625 - 2, 1, 0x0504, 0x0000}, + {858 - 24, 20, 525 - 2, 1, 0x00f8, 0x0000}, +}; + +/* ----------------------------------------------------------------------- */ + +static inline int bt819_write(struct bt819 *decoder, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd); + + decoder->reg[reg] = value; + return i2c_smbus_write_byte_data(client, reg, value); +} + +static inline int bt819_setbit(struct bt819 *decoder, u8 reg, u8 bit, u8 value) +{ + return bt819_write(decoder, reg, + (decoder->reg[reg] & ~(1 << bit)) | (value ? (1 << bit) : 0)); +} + +static int bt819_write_block(struct bt819 *decoder, const u8 *data, unsigned int len) +{ + struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd); + int ret = -1; + u8 reg; + + /* the bt819 has an autoincrement function, use it if + * the adapter understands raw I2C */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + /* do raw I2C, not smbus compatible */ + u8 block_data[32]; + int block_len; + + while (len >= 2) { + block_len = 0; + block_data[block_len++] = reg = data[0]; + do { + block_data[block_len++] = + decoder->reg[reg++] = data[1]; + len -= 2; + data += 2; + } while (len >= 2 && data[0] == reg && block_len < 32); + ret = i2c_master_send(client, block_data, block_len); + if (ret < 0) + break; + } + } else { + /* do some slow I2C emulation kind of thing */ + while (len >= 2) { + reg = *data++; + ret = bt819_write(decoder, reg, *data++); + if (ret < 0) + break; + len -= 2; + } + } + + return ret; +} + +static inline int bt819_read(struct bt819 *decoder, u8 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd); + + return i2c_smbus_read_byte_data(client, reg); +} + +static int bt819_init(struct v4l2_subdev *sd) +{ + static unsigned char init[] = { + /*0x1f, 0x00,*/ /* Reset */ + 0x01, 0x59, /* 0x01 input format */ + 0x02, 0x00, /* 0x02 temporal decimation */ + 0x03, 0x12, /* 0x03 Cropping msb */ + 0x04, 0x16, /* 0x04 Vertical Delay, lsb */ + 0x05, 0xe0, /* 0x05 Vertical Active lsb */ + 0x06, 0x80, /* 0x06 Horizontal Delay lsb */ + 0x07, 0xd0, /* 0x07 Horizontal Active lsb */ + 0x08, 0x00, /* 0x08 Horizontal Scaling msb */ + 0x09, 0xf8, /* 0x09 Horizontal Scaling lsb */ + 0x0a, 0x00, /* 0x0a Brightness control */ + 0x0b, 0x30, /* 0x0b Miscellaneous control */ + 0x0c, 0xd8, /* 0x0c Luma Gain lsb */ + 0x0d, 0xfe, /* 0x0d Chroma Gain (U) lsb */ + 0x0e, 0xb4, /* 0x0e Chroma Gain (V) msb */ + 0x0f, 0x00, /* 0x0f Hue control */ + 0x12, 0x04, /* 0x12 Output Format */ + 0x13, 0x20, /* 0x13 Vertial Scaling msb 0x00 + chroma comb OFF, line drop scaling, interlace scaling + BUG? Why does turning the chroma comb on fuck up color? + Bug in the bt819 stepping on my board? + */ + 0x14, 0x00, /* 0x14 Vertial Scaling lsb */ + 0x16, 0x07, /* 0x16 Video Timing Polarity + ACTIVE=active low + FIELD: high=odd, + vreset=active high, + hreset=active high */ + 0x18, 0x68, /* 0x18 AGC Delay */ + 0x19, 0x5d, /* 0x19 Burst Gate Delay */ + 0x1a, 0x80, /* 0x1a ADC Interface */ + }; + + struct bt819 *decoder = to_bt819(sd); + struct timing *timing = &timing_data[(decoder->norm & V4L2_STD_525_60) ? 1 : 0]; + + init[0x03 * 2 - 1] = + (((timing->vdelay >> 8) & 0x03) << 6) | + (((timing->vactive >> 8) & 0x03) << 4) | + (((timing->hdelay >> 8) & 0x03) << 2) | + ((timing->hactive >> 8) & 0x03); + init[0x04 * 2 - 1] = timing->vdelay & 0xff; + init[0x05 * 2 - 1] = timing->vactive & 0xff; + init[0x06 * 2 - 1] = timing->hdelay & 0xff; + init[0x07 * 2 - 1] = timing->hactive & 0xff; + init[0x08 * 2 - 1] = timing->hscale >> 8; + init[0x09 * 2 - 1] = timing->hscale & 0xff; + /* 0x15 in array is address 0x19 */ + init[0x15 * 2 - 1] = (decoder->norm & V4L2_STD_625_50) ? 115 : 93; /* Chroma burst delay */ + /* reset */ + bt819_write(decoder, 0x1f, 0x00); + mdelay(1); + + /* init */ + return bt819_write_block(decoder, init, sizeof(init)); +} + +/* ----------------------------------------------------------------------- */ + +static int bt819_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd) +{ + struct bt819 *decoder = to_bt819(sd); + int status = bt819_read(decoder, 0x00); + int res = V4L2_IN_ST_NO_SIGNAL; + v4l2_std_id std; + + if ((status & 0x80)) + res = 0; + + if ((status & 0x10)) + std = V4L2_STD_PAL; + else + std = V4L2_STD_NTSC; + if (pstd) + *pstd = std; + if (pstatus) + *pstatus = res; + + v4l2_dbg(1, debug, sd, "get status %x\n", status); + return 0; +} + +static int bt819_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + return bt819_status(sd, NULL, std); +} + +static int bt819_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + return bt819_status(sd, status, NULL); +} + +static int bt819_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct bt819 *decoder = to_bt819(sd); + struct timing *timing = NULL; + + v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); + + if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL) + v4l2_err(sd, "no notify found!\n"); + + if (std & V4L2_STD_NTSC) { + v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL); + bt819_setbit(decoder, 0x01, 0, 1); + bt819_setbit(decoder, 0x01, 1, 0); + bt819_setbit(decoder, 0x01, 5, 0); + bt819_write(decoder, 0x18, 0x68); + bt819_write(decoder, 0x19, 0x5d); + /* bt819_setbit(decoder, 0x1a, 5, 1); */ + timing = &timing_data[1]; + } else if (std & V4L2_STD_PAL) { + v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL); + bt819_setbit(decoder, 0x01, 0, 1); + bt819_setbit(decoder, 0x01, 1, 1); + bt819_setbit(decoder, 0x01, 5, 1); + bt819_write(decoder, 0x18, 0x7f); + bt819_write(decoder, 0x19, 0x72); + /* bt819_setbit(decoder, 0x1a, 5, 0); */ + timing = &timing_data[0]; + } else { + v4l2_dbg(1, debug, sd, "unsupported norm %llx\n", + (unsigned long long)std); + return -EINVAL; + } + bt819_write(decoder, 0x03, + (((timing->vdelay >> 8) & 0x03) << 6) | + (((timing->vactive >> 8) & 0x03) << 4) | + (((timing->hdelay >> 8) & 0x03) << 2) | + ((timing->hactive >> 8) & 0x03)); + bt819_write(decoder, 0x04, timing->vdelay & 0xff); + bt819_write(decoder, 0x05, timing->vactive & 0xff); + bt819_write(decoder, 0x06, timing->hdelay & 0xff); + bt819_write(decoder, 0x07, timing->hactive & 0xff); + bt819_write(decoder, 0x08, (timing->hscale >> 8) & 0xff); + bt819_write(decoder, 0x09, timing->hscale & 0xff); + decoder->norm = std; + v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, NULL); + return 0; +} + +static int bt819_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct bt819 *decoder = to_bt819(sd); + + v4l2_dbg(1, debug, sd, "set input %x\n", input); + + if (input > 7) + return -EINVAL; + + if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL) + v4l2_err(sd, "no notify found!\n"); + + if (decoder->input != input) { + v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL); + decoder->input = input; + /* select mode */ + if (decoder->input == 0) { + bt819_setbit(decoder, 0x0b, 6, 0); + bt819_setbit(decoder, 0x1a, 1, 1); + } else { + bt819_setbit(decoder, 0x0b, 6, 1); + bt819_setbit(decoder, 0x1a, 1, 0); + } + v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, NULL); + } + return 0; +} + +static int bt819_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct bt819 *decoder = to_bt819(sd); + + v4l2_dbg(1, debug, sd, "enable output %x\n", enable); + + if (decoder->enable != enable) { + decoder->enable = enable; + bt819_setbit(decoder, 0x16, 7, !enable); + } + return 0; +} + +static int bt819_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct bt819 *decoder = to_bt819(sd); + int temp; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + bt819_write(decoder, 0x0a, ctrl->val); + break; + + case V4L2_CID_CONTRAST: + bt819_write(decoder, 0x0c, ctrl->val & 0xff); + bt819_setbit(decoder, 0x0b, 2, ((ctrl->val >> 8) & 0x01)); + break; + + case V4L2_CID_SATURATION: + bt819_write(decoder, 0x0d, (ctrl->val >> 7) & 0xff); + bt819_setbit(decoder, 0x0b, 1, ((ctrl->val >> 15) & 0x01)); + + /* Ratio between U gain and V gain must stay the same as + the ratio between the default U and V gain values. */ + temp = (ctrl->val * 180) / 254; + bt819_write(decoder, 0x0e, (temp >> 7) & 0xff); + bt819_setbit(decoder, 0x0b, 0, (temp >> 15) & 0x01); + break; + + case V4L2_CID_HUE: + bt819_write(decoder, 0x0f, ctrl->val); + break; + + default: + return -EINVAL; + } + return 0; +} + +static int bt819_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct bt819 *decoder = to_bt819(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, decoder->ident, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops bt819_ctrl_ops = { + .s_ctrl = bt819_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops bt819_core_ops = { + .g_chip_ident = bt819_g_chip_ident, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, + .s_std = bt819_s_std, +}; + +static const struct v4l2_subdev_video_ops bt819_video_ops = { + .s_routing = bt819_s_routing, + .s_stream = bt819_s_stream, + .querystd = bt819_querystd, + .g_input_status = bt819_g_input_status, +}; + +static const struct v4l2_subdev_ops bt819_ops = { + .core = &bt819_core_ops, + .video = &bt819_video_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static int bt819_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int i, ver; + struct bt819 *decoder; + struct v4l2_subdev *sd; + const char *name; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + decoder = kzalloc(sizeof(struct bt819), GFP_KERNEL); + if (decoder == NULL) + return -ENOMEM; + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &bt819_ops); + + ver = bt819_read(decoder, 0x17); + switch (ver & 0xf0) { + case 0x70: + name = "bt819a"; + decoder->ident = V4L2_IDENT_BT819A; + break; + case 0x60: + name = "bt817a"; + decoder->ident = V4L2_IDENT_BT817A; + break; + case 0x20: + name = "bt815a"; + decoder->ident = V4L2_IDENT_BT815A; + break; + default: + v4l2_dbg(1, debug, sd, + "unknown chip version 0x%02x\n", ver); + return -ENODEV; + } + + v4l_info(client, "%s found @ 0x%x (%s)\n", name, + client->addr << 1, client->adapter->name); + + decoder->norm = V4L2_STD_NTSC; + decoder->input = 0; + decoder->enable = 1; + + i = bt819_init(sd); + if (i < 0) + v4l2_dbg(1, debug, sd, "init status %d\n", i); + + v4l2_ctrl_handler_init(&decoder->hdl, 4); + v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops, + V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); + v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops, + V4L2_CID_CONTRAST, 0, 511, 1, 0xd8); + v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops, + V4L2_CID_SATURATION, 0, 511, 1, 0xfe); + v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + sd->ctrl_handler = &decoder->hdl; + if (decoder->hdl.error) { + int err = decoder->hdl.error; + + v4l2_ctrl_handler_free(&decoder->hdl); + kfree(decoder); + return err; + } + v4l2_ctrl_handler_setup(&decoder->hdl); + return 0; +} + +static int bt819_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct bt819 *decoder = to_bt819(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&decoder->hdl); + kfree(decoder); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id bt819_id[] = { + { "bt819a", 0 }, + { "bt817a", 0 }, + { "bt815a", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, bt819_id); + +static struct i2c_driver bt819_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "bt819", + }, + .probe = bt819_probe, + .remove = bt819_remove, + .id_table = bt819_id, +}; + +module_i2c_driver(bt819_driver); diff --git a/drivers/media/i2c/bt856.c b/drivers/media/i2c/bt856.c new file mode 100644 index 00000000000..7e5bd365c23 --- /dev/null +++ b/drivers/media/i2c/bt856.c @@ -0,0 +1,273 @@ +/* + * bt856 - BT856A Digital Video Encoder (Rockwell Part) + * + * Copyright (C) 1999 Mike Bernson + * Copyright (C) 1998 Dave Perks + * + * Modifications for LML33/DC10plus unified driver + * Copyright (C) 2000 Serguei Miridonov + * + * This code was modify/ported from the saa7111 driver written + * by Dave Perks. + * + * Changes by Ronald Bultje + * - moved over to linux>=2.4.x i2c protocol (9/9/2002) + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("Brooktree-856A video encoder driver"); +MODULE_AUTHOR("Mike Bernson & Dave Perks"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + + +/* ----------------------------------------------------------------------- */ + +#define BT856_REG_OFFSET 0xDA +#define BT856_NR_REG 6 + +struct bt856 { + struct v4l2_subdev sd; + unsigned char reg[BT856_NR_REG]; + + v4l2_std_id norm; +}; + +static inline struct bt856 *to_bt856(struct v4l2_subdev *sd) +{ + return container_of(sd, struct bt856, sd); +} + +/* ----------------------------------------------------------------------- */ + +static inline int bt856_write(struct bt856 *encoder, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(&encoder->sd); + + encoder->reg[reg - BT856_REG_OFFSET] = value; + return i2c_smbus_write_byte_data(client, reg, value); +} + +static inline int bt856_setbit(struct bt856 *encoder, u8 reg, u8 bit, u8 value) +{ + return bt856_write(encoder, reg, + (encoder->reg[reg - BT856_REG_OFFSET] & ~(1 << bit)) | + (value ? (1 << bit) : 0)); +} + +static void bt856_dump(struct bt856 *encoder) +{ + int i; + + v4l2_info(&encoder->sd, "register dump:\n"); + for (i = 0; i < BT856_NR_REG; i += 2) + printk(KERN_CONT " %02x", encoder->reg[i]); + printk(KERN_CONT "\n"); +} + +/* ----------------------------------------------------------------------- */ + +static int bt856_init(struct v4l2_subdev *sd, u32 arg) +{ + struct bt856 *encoder = to_bt856(sd); + + /* This is just for testing!!! */ + v4l2_dbg(1, debug, sd, "init\n"); + bt856_write(encoder, 0xdc, 0x18); + bt856_write(encoder, 0xda, 0); + bt856_write(encoder, 0xde, 0); + + bt856_setbit(encoder, 0xdc, 3, 1); + /*bt856_setbit(encoder, 0xdc, 6, 0);*/ + bt856_setbit(encoder, 0xdc, 4, 1); + + if (encoder->norm & V4L2_STD_NTSC) + bt856_setbit(encoder, 0xdc, 2, 0); + else + bt856_setbit(encoder, 0xdc, 2, 1); + + bt856_setbit(encoder, 0xdc, 1, 1); + bt856_setbit(encoder, 0xde, 4, 0); + bt856_setbit(encoder, 0xde, 3, 1); + if (debug != 0) + bt856_dump(encoder); + return 0; +} + +static int bt856_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct bt856 *encoder = to_bt856(sd); + + v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); + + if (std & V4L2_STD_NTSC) { + bt856_setbit(encoder, 0xdc, 2, 0); + } else if (std & V4L2_STD_PAL) { + bt856_setbit(encoder, 0xdc, 2, 1); + bt856_setbit(encoder, 0xda, 0, 0); + /*bt856_setbit(encoder, 0xda, 0, 1);*/ + } else { + return -EINVAL; + } + encoder->norm = std; + if (debug != 0) + bt856_dump(encoder); + return 0; +} + +static int bt856_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct bt856 *encoder = to_bt856(sd); + + v4l2_dbg(1, debug, sd, "set input %d\n", input); + + /* We only have video bus. + * input= 0: input is from bt819 + * input= 1: input is from ZR36060 */ + switch (input) { + case 0: + bt856_setbit(encoder, 0xde, 4, 0); + bt856_setbit(encoder, 0xde, 3, 1); + bt856_setbit(encoder, 0xdc, 3, 1); + bt856_setbit(encoder, 0xdc, 6, 0); + break; + case 1: + bt856_setbit(encoder, 0xde, 4, 0); + bt856_setbit(encoder, 0xde, 3, 1); + bt856_setbit(encoder, 0xdc, 3, 1); + bt856_setbit(encoder, 0xdc, 6, 1); + break; + case 2: /* Color bar */ + bt856_setbit(encoder, 0xdc, 3, 0); + bt856_setbit(encoder, 0xde, 4, 1); + break; + default: + return -EINVAL; + } + + if (debug != 0) + bt856_dump(encoder); + return 0; +} + +static int bt856_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_BT856, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops bt856_core_ops = { + .g_chip_ident = bt856_g_chip_ident, + .init = bt856_init, +}; + +static const struct v4l2_subdev_video_ops bt856_video_ops = { + .s_std_output = bt856_s_std_output, + .s_routing = bt856_s_routing, +}; + +static const struct v4l2_subdev_ops bt856_ops = { + .core = &bt856_core_ops, + .video = &bt856_video_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static int bt856_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct bt856 *encoder; + struct v4l2_subdev *sd; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + encoder = kzalloc(sizeof(struct bt856), GFP_KERNEL); + if (encoder == NULL) + return -ENOMEM; + sd = &encoder->sd; + v4l2_i2c_subdev_init(sd, client, &bt856_ops); + encoder->norm = V4L2_STD_NTSC; + + bt856_write(encoder, 0xdc, 0x18); + bt856_write(encoder, 0xda, 0); + bt856_write(encoder, 0xde, 0); + + bt856_setbit(encoder, 0xdc, 3, 1); + /*bt856_setbit(encoder, 0xdc, 6, 0);*/ + bt856_setbit(encoder, 0xdc, 4, 1); + + if (encoder->norm & V4L2_STD_NTSC) + bt856_setbit(encoder, 0xdc, 2, 0); + else + bt856_setbit(encoder, 0xdc, 2, 1); + + bt856_setbit(encoder, 0xdc, 1, 1); + bt856_setbit(encoder, 0xde, 4, 0); + bt856_setbit(encoder, 0xde, 3, 1); + + if (debug != 0) + bt856_dump(encoder); + return 0; +} + +static int bt856_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_bt856(sd)); + return 0; +} + +static const struct i2c_device_id bt856_id[] = { + { "bt856", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, bt856_id); + +static struct i2c_driver bt856_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "bt856", + }, + .probe = bt856_probe, + .remove = bt856_remove, + .id_table = bt856_id, +}; + +module_i2c_driver(bt856_driver); diff --git a/drivers/media/i2c/bt866.c b/drivers/media/i2c/bt866.c new file mode 100644 index 00000000000..905320b67a1 --- /dev/null +++ b/drivers/media/i2c/bt866.c @@ -0,0 +1,243 @@ +/* + bt866 - BT866 Digital Video Encoder (Rockwell Part) + + Copyright (C) 1999 Mike Bernson + Copyright (C) 1998 Dave Perks + + Modifications for LML33/DC10plus unified driver + Copyright (C) 2000 Serguei Miridonov + + This code was modify/ported from the saa7111 driver written + by Dave Perks. + + This code was adapted for the bt866 by Christer Weinigel and ported + to 2.6 by Martin Samuelsson. + + 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 +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("Brooktree-866 video encoder driver"); +MODULE_AUTHOR("Mike Bernson & Dave Perks"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + + +/* ----------------------------------------------------------------------- */ + +struct bt866 { + struct v4l2_subdev sd; + u8 reg[256]; +}; + +static inline struct bt866 *to_bt866(struct v4l2_subdev *sd) +{ + return container_of(sd, struct bt866, sd); +} + +static int bt866_write(struct bt866 *encoder, u8 subaddr, u8 data) +{ + struct i2c_client *client = v4l2_get_subdevdata(&encoder->sd); + u8 buffer[2]; + int err; + + buffer[0] = subaddr; + buffer[1] = data; + + encoder->reg[subaddr] = data; + + v4l_dbg(1, debug, client, "write 0x%02x = 0x%02x\n", subaddr, data); + + for (err = 0; err < 3;) { + if (i2c_master_send(client, buffer, 2) == 2) + break; + err++; + v4l_warn(client, "error #%d writing to 0x%02x\n", + err, subaddr); + schedule_timeout_interruptible(msecs_to_jiffies(100)); + } + if (err == 3) { + v4l_warn(client, "giving up\n"); + return -1; + } + + return 0; +} + +static int bt866_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) +{ + v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); + + /* Only PAL supported by this driver at the moment! */ + if (!(std & V4L2_STD_NTSC)) + return -EINVAL; + return 0; +} + +static int bt866_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + static const __u8 init[] = { + 0xc8, 0xcc, /* CRSCALE */ + 0xca, 0x91, /* CBSCALE */ + 0xcc, 0x24, /* YC16 | OSDNUM */ + 0xda, 0x00, /* */ + 0xdc, 0x24, /* SETMODE | PAL */ + 0xde, 0x02, /* EACTIVE */ + + /* overlay colors */ + 0x70, 0xEB, 0x90, 0x80, 0xB0, 0x80, /* white */ + 0x72, 0xA2, 0x92, 0x8E, 0xB2, 0x2C, /* yellow */ + 0x74, 0x83, 0x94, 0x2C, 0xB4, 0x9C, /* cyan */ + 0x76, 0x70, 0x96, 0x3A, 0xB6, 0x48, /* green */ + 0x78, 0x54, 0x98, 0xC6, 0xB8, 0xB8, /* magenta */ + 0x7A, 0x41, 0x9A, 0xD4, 0xBA, 0x64, /* red */ + 0x7C, 0x23, 0x9C, 0x72, 0xBC, 0xD4, /* blue */ + 0x7E, 0x10, 0x9E, 0x80, 0xBE, 0x80, /* black */ + + 0x60, 0xEB, 0x80, 0x80, 0xc0, 0x80, /* white */ + 0x62, 0xA2, 0x82, 0x8E, 0xc2, 0x2C, /* yellow */ + 0x64, 0x83, 0x84, 0x2C, 0xc4, 0x9C, /* cyan */ + 0x66, 0x70, 0x86, 0x3A, 0xc6, 0x48, /* green */ + 0x68, 0x54, 0x88, 0xC6, 0xc8, 0xB8, /* magenta */ + 0x6A, 0x41, 0x8A, 0xD4, 0xcA, 0x64, /* red */ + 0x6C, 0x23, 0x8C, 0x72, 0xcC, 0xD4, /* blue */ + 0x6E, 0x10, 0x8E, 0x80, 0xcE, 0x80, /* black */ + }; + struct bt866 *encoder = to_bt866(sd); + u8 val; + int i; + + for (i = 0; i < ARRAY_SIZE(init) / 2; i += 2) + bt866_write(encoder, init[i], init[i+1]); + + val = encoder->reg[0xdc]; + + if (input == 0) + val |= 0x40; /* CBSWAP */ + else + val &= ~0x40; /* !CBSWAP */ + + bt866_write(encoder, 0xdc, val); + + val = encoder->reg[0xcc]; + if (input == 2) + val |= 0x01; /* OSDBAR */ + else + val &= ~0x01; /* !OSDBAR */ + bt866_write(encoder, 0xcc, val); + + v4l2_dbg(1, debug, sd, "set input %d\n", input); + + switch (input) { + case 0: + case 1: + case 2: + break; + default: + return -EINVAL; + } + return 0; +} + +#if 0 +/* Code to setup square pixels, might be of some use in the future, + but is currently unused. */ + val = encoder->reg[0xdc]; + if (*iarg) + val |= 1; /* SQUARE */ + else + val &= ~1; /* !SQUARE */ + bt866_write(client, 0xdc, val); +#endif + +static int bt866_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_BT866, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops bt866_core_ops = { + .g_chip_ident = bt866_g_chip_ident, +}; + +static const struct v4l2_subdev_video_ops bt866_video_ops = { + .s_std_output = bt866_s_std_output, + .s_routing = bt866_s_routing, +}; + +static const struct v4l2_subdev_ops bt866_ops = { + .core = &bt866_core_ops, + .video = &bt866_video_ops, +}; + +static int bt866_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct bt866 *encoder; + struct v4l2_subdev *sd; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + encoder = kzalloc(sizeof(*encoder), GFP_KERNEL); + if (encoder == NULL) + return -ENOMEM; + sd = &encoder->sd; + v4l2_i2c_subdev_init(sd, client, &bt866_ops); + return 0; +} + +static int bt866_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_bt866(sd)); + return 0; +} + +static const struct i2c_device_id bt866_id[] = { + { "bt866", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, bt866_id); + +static struct i2c_driver bt866_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "bt866", + }, + .probe = bt866_probe, + .remove = bt866_remove, + .id_table = bt866_id, +}; + +module_i2c_driver(bt866_driver); diff --git a/drivers/media/i2c/btcx-risc.c b/drivers/media/i2c/btcx-risc.c new file mode 100644 index 00000000000..ac1b2687a20 --- /dev/null +++ b/drivers/media/i2c/btcx-risc.c @@ -0,0 +1,260 @@ +/* + + btcx-risc.c + + bt848/bt878/cx2388x risc code generator. + + (c) 2000-03 Gerd Knorr [SuSE Labs] + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + 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 +#include +#include +#include +#include +#include +#include + +#include "btcx-risc.h" + +MODULE_DESCRIPTION("some code shared by bttv and cx88xx drivers"); +MODULE_AUTHOR("Gerd Knorr"); +MODULE_LICENSE("GPL"); + +static unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug,"debug messages, default is 0 (no)"); + +/* ---------------------------------------------------------- */ +/* allocate/free risc memory */ + +static int memcnt; + +void btcx_riscmem_free(struct pci_dev *pci, + struct btcx_riscmem *risc) +{ + if (NULL == risc->cpu) + return; + if (debug) { + memcnt--; + printk("btcx: riscmem free [%d] dma=%lx\n", + memcnt, (unsigned long)risc->dma); + } + pci_free_consistent(pci, risc->size, risc->cpu, risc->dma); + memset(risc,0,sizeof(*risc)); +} + +int btcx_riscmem_alloc(struct pci_dev *pci, + struct btcx_riscmem *risc, + unsigned int size) +{ + __le32 *cpu; + dma_addr_t dma = 0; + + if (NULL != risc->cpu && risc->size < size) + btcx_riscmem_free(pci,risc); + if (NULL == risc->cpu) { + cpu = pci_alloc_consistent(pci, size, &dma); + if (NULL == cpu) + return -ENOMEM; + risc->cpu = cpu; + risc->dma = dma; + risc->size = size; + if (debug) { + memcnt++; + printk("btcx: riscmem alloc [%d] dma=%lx cpu=%p size=%d\n", + memcnt, (unsigned long)dma, cpu, size); + } + } + memset(risc->cpu,0,risc->size); + return 0; +} + +/* ---------------------------------------------------------- */ +/* screen overlay helpers */ + +int +btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win, + struct v4l2_clip *clips, unsigned int n) +{ + if (win->left < 0) { + /* left */ + clips[n].c.left = 0; + clips[n].c.top = 0; + clips[n].c.width = -win->left; + clips[n].c.height = win->height; + n++; + } + if (win->left + win->width > swidth) { + /* right */ + clips[n].c.left = swidth - win->left; + clips[n].c.top = 0; + clips[n].c.width = win->width - clips[n].c.left; + clips[n].c.height = win->height; + n++; + } + if (win->top < 0) { + /* top */ + clips[n].c.left = 0; + clips[n].c.top = 0; + clips[n].c.width = win->width; + clips[n].c.height = -win->top; + n++; + } + if (win->top + win->height > sheight) { + /* bottom */ + clips[n].c.left = 0; + clips[n].c.top = sheight - win->top; + clips[n].c.width = win->width; + clips[n].c.height = win->height - clips[n].c.top; + n++; + } + return n; +} + +int +btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, unsigned int n, int mask) +{ + s32 nx,nw,dx; + unsigned int i; + + /* fixup window */ + nx = (win->left + mask) & ~mask; + nw = (win->width) & ~mask; + if (nx + nw > win->left + win->width) + nw -= mask+1; + dx = nx - win->left; + win->left = nx; + win->width = nw; + if (debug) + printk(KERN_DEBUG "btcx: window align %dx%d+%d+%d [dx=%d]\n", + win->width, win->height, win->left, win->top, dx); + + /* fixup clips */ + for (i = 0; i < n; i++) { + nx = (clips[i].c.left-dx) & ~mask; + nw = (clips[i].c.width) & ~mask; + if (nx + nw < clips[i].c.left-dx + clips[i].c.width) + nw += mask+1; + clips[i].c.left = nx; + clips[i].c.width = nw; + if (debug) + printk(KERN_DEBUG "btcx: clip align %dx%d+%d+%d\n", + clips[i].c.width, clips[i].c.height, + clips[i].c.left, clips[i].c.top); + } + return 0; +} + +void +btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips) +{ + struct v4l2_clip swap; + int i,j,n; + + if (nclips < 2) + return; + for (i = nclips-2; i >= 0; i--) { + for (n = 0, j = 0; j <= i; j++) { + if (clips[j].c.left > clips[j+1].c.left) { + swap = clips[j]; + clips[j] = clips[j+1]; + clips[j+1] = swap; + n++; + } + } + if (0 == n) + break; + } +} + +void +btcx_calc_skips(int line, int width, int *maxy, + struct btcx_skiplist *skips, unsigned int *nskips, + const struct v4l2_clip *clips, unsigned int nclips) +{ + unsigned int clip,skip; + int end, maxline; + + skip=0; + maxline = 9999; + for (clip = 0; clip < nclips; clip++) { + + /* sanity checks */ + if (clips[clip].c.left + clips[clip].c.width <= 0) + continue; + if (clips[clip].c.left > (signed)width) + break; + + /* vertical range */ + if (line > clips[clip].c.top+clips[clip].c.height-1) + continue; + if (line < clips[clip].c.top) { + if (maxline > clips[clip].c.top-1) + maxline = clips[clip].c.top-1; + continue; + } + if (maxline > clips[clip].c.top+clips[clip].c.height-1) + maxline = clips[clip].c.top+clips[clip].c.height-1; + + /* horizontal range */ + if (0 == skip || clips[clip].c.left > skips[skip-1].end) { + /* new one */ + skips[skip].start = clips[clip].c.left; + if (skips[skip].start < 0) + skips[skip].start = 0; + skips[skip].end = clips[clip].c.left + clips[clip].c.width; + if (skips[skip].end > width) + skips[skip].end = width; + skip++; + } else { + /* overlaps -- expand last one */ + end = clips[clip].c.left + clips[clip].c.width; + if (skips[skip-1].end < end) + skips[skip-1].end = end; + if (skips[skip-1].end > width) + skips[skip-1].end = width; + } + } + *nskips = skip; + *maxy = maxline; + + if (debug) { + printk(KERN_DEBUG "btcx: skips line %d-%d:",line,maxline); + for (skip = 0; skip < *nskips; skip++) { + printk(" %d-%d",skips[skip].start,skips[skip].end); + } + printk("\n"); + } +} + +/* ---------------------------------------------------------- */ + +EXPORT_SYMBOL(btcx_riscmem_alloc); +EXPORT_SYMBOL(btcx_riscmem_free); + +EXPORT_SYMBOL(btcx_screen_clips); +EXPORT_SYMBOL(btcx_align); +EXPORT_SYMBOL(btcx_sort_clips); +EXPORT_SYMBOL(btcx_calc_skips); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/i2c/btcx-risc.h b/drivers/media/i2c/btcx-risc.h new file mode 100644 index 00000000000..f8bc6e8e7b5 --- /dev/null +++ b/drivers/media/i2c/btcx-risc.h @@ -0,0 +1,34 @@ +/* + */ +struct btcx_riscmem { + unsigned int size; + __le32 *cpu; + __le32 *jmp; + dma_addr_t dma; +}; + +struct btcx_skiplist { + int start; + int end; +}; + +int btcx_riscmem_alloc(struct pci_dev *pci, + struct btcx_riscmem *risc, + unsigned int size); +void btcx_riscmem_free(struct pci_dev *pci, + struct btcx_riscmem *risc); + +int btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win, + struct v4l2_clip *clips, unsigned int n); +int btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, + unsigned int n, int mask); +void btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips); +void btcx_calc_skips(int line, int width, int *maxy, + struct btcx_skiplist *skips, unsigned int *nskips, + const struct v4l2_clip *clips, unsigned int nclips); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/i2c/cs5345.c b/drivers/media/i2c/cs5345.c new file mode 100644 index 00000000000..c8581e26fa9 --- /dev/null +++ b/drivers/media/i2c/cs5345.c @@ -0,0 +1,252 @@ +/* + * cs5345 Cirrus Logic 24-bit, 192 kHz Stereo Audio ADC + * Copyright (C) 2007 Hans Verkuil + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("i2c device driver for cs5345 Audio ADC"); +MODULE_AUTHOR("Hans Verkuil"); +MODULE_LICENSE("GPL"); + +static bool debug; + +module_param(debug, bool, 0644); + +MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On"); + +struct cs5345_state { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; +}; + +static inline struct cs5345_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct cs5345_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct cs5345_state, hdl)->sd; +} + +/* ----------------------------------------------------------------------- */ + +static inline int cs5345_write(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_write_byte_data(client, reg, value); +} + +static inline int cs5345_read(struct v4l2_subdev *sd, u8 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_read_byte_data(client, reg); +} + +static int cs5345_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + if ((input & 0xf) > 6) { + v4l2_err(sd, "Invalid input %d.\n", input); + return -EINVAL; + } + cs5345_write(sd, 0x09, input & 0xf); + cs5345_write(sd, 0x05, input & 0xf0); + return 0; +} + +static int cs5345_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + cs5345_write(sd, 0x04, ctrl->val ? 0x80 : 0); + return 0; + case V4L2_CID_AUDIO_VOLUME: + cs5345_write(sd, 0x07, ((u8)ctrl->val) & 0x3f); + cs5345_write(sd, 0x08, ((u8)ctrl->val) & 0x3f); + return 0; + } + return -EINVAL; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int cs5345_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->size = 1; + reg->val = cs5345_read(sd, reg->reg & 0x1f); + return 0; +} + +static int cs5345_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + cs5345_write(sd, reg->reg & 0x1f, reg->val & 0xff); + return 0; +} +#endif + +static int cs5345_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_CS5345, 0); +} + +static int cs5345_log_status(struct v4l2_subdev *sd) +{ + u8 v = cs5345_read(sd, 0x09) & 7; + u8 m = cs5345_read(sd, 0x04); + int vol = cs5345_read(sd, 0x08) & 0x3f; + + v4l2_info(sd, "Input: %d%s\n", v, + (m & 0x80) ? " (muted)" : ""); + if (vol >= 32) + vol = vol - 64; + v4l2_info(sd, "Volume: %d dB\n", vol); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops cs5345_ctrl_ops = { + .s_ctrl = cs5345_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops cs5345_core_ops = { + .log_status = cs5345_log_status, + .g_chip_ident = cs5345_g_chip_ident, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = cs5345_g_register, + .s_register = cs5345_s_register, +#endif +}; + +static const struct v4l2_subdev_audio_ops cs5345_audio_ops = { + .s_routing = cs5345_s_routing, +}; + +static const struct v4l2_subdev_ops cs5345_ops = { + .core = &cs5345_core_ops, + .audio = &cs5345_audio_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static int cs5345_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cs5345_state *state; + struct v4l2_subdev *sd; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct cs5345_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &cs5345_ops); + + v4l2_ctrl_handler_init(&state->hdl, 2); + v4l2_ctrl_new_std(&state->hdl, &cs5345_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + v4l2_ctrl_new_std(&state->hdl, &cs5345_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, -24, 24, 1, 0); + sd->ctrl_handler = &state->hdl; + if (state->hdl.error) { + int err = state->hdl.error; + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + /* set volume/mute */ + v4l2_ctrl_handler_setup(&state->hdl); + + cs5345_write(sd, 0x02, 0x00); + cs5345_write(sd, 0x04, 0x01); + cs5345_write(sd, 0x09, 0x01); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int cs5345_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct cs5345_state *state = to_state(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id cs5345_id[] = { + { "cs5345", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cs5345_id); + +static struct i2c_driver cs5345_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "cs5345", + }, + .probe = cs5345_probe, + .remove = cs5345_remove, + .id_table = cs5345_id, +}; + +module_i2c_driver(cs5345_driver); diff --git a/drivers/media/i2c/cs53l32a.c b/drivers/media/i2c/cs53l32a.c new file mode 100644 index 00000000000..b293912206e --- /dev/null +++ b/drivers/media/i2c/cs53l32a.c @@ -0,0 +1,251 @@ +/* + * cs53l32a (Adaptec AVC-2010 and AVC-2410) i2c ivtv driver. + * Copyright (C) 2005 Martin Vaughan + * + * Audio source switching for Adaptec AVC-2410 added by Trev Jackson + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("i2c device driver for cs53l32a Audio ADC"); +MODULE_AUTHOR("Martin Vaughan"); +MODULE_LICENSE("GPL"); + +static bool debug; + +module_param(debug, bool, 0644); + +MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On"); + + +struct cs53l32a_state { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; +}; + +static inline struct cs53l32a_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct cs53l32a_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct cs53l32a_state, hdl)->sd; +} + +/* ----------------------------------------------------------------------- */ + +static int cs53l32a_write(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_write_byte_data(client, reg, value); +} + +static int cs53l32a_read(struct v4l2_subdev *sd, u8 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_read_byte_data(client, reg); +} + +static int cs53l32a_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + /* There are 2 physical inputs, but the second input can be + placed in two modes, the first mode bypasses the PGA (gain), + the second goes through the PGA. Hence there are three + possible inputs to choose from. */ + if (input > 2) { + v4l2_err(sd, "Invalid input %d.\n", input); + return -EINVAL; + } + cs53l32a_write(sd, 0x01, 0x01 + (input << 4)); + return 0; +} + +static int cs53l32a_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + cs53l32a_write(sd, 0x03, ctrl->val ? 0xf0 : 0x30); + return 0; + case V4L2_CID_AUDIO_VOLUME: + cs53l32a_write(sd, 0x04, (u8)ctrl->val); + cs53l32a_write(sd, 0x05, (u8)ctrl->val); + return 0; + } + return -EINVAL; +} + +static int cs53l32a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, + chip, V4L2_IDENT_CS53l32A, 0); +} + +static int cs53l32a_log_status(struct v4l2_subdev *sd) +{ + struct cs53l32a_state *state = to_state(sd); + u8 v = cs53l32a_read(sd, 0x01); + + v4l2_info(sd, "Input: %d\n", (v >> 4) & 3); + v4l2_ctrl_handler_log_status(&state->hdl, sd->name); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops cs53l32a_ctrl_ops = { + .s_ctrl = cs53l32a_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops cs53l32a_core_ops = { + .log_status = cs53l32a_log_status, + .g_chip_ident = cs53l32a_g_chip_ident, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, +}; + +static const struct v4l2_subdev_audio_ops cs53l32a_audio_ops = { + .s_routing = cs53l32a_s_routing, +}; + +static const struct v4l2_subdev_ops cs53l32a_ops = { + .core = &cs53l32a_core_ops, + .audio = &cs53l32a_audio_ops, +}; + +/* ----------------------------------------------------------------------- */ + +/* i2c implementation */ + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ + +static int cs53l32a_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cs53l32a_state *state; + struct v4l2_subdev *sd; + int i; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + if (!id) + strlcpy(client->name, "cs53l32a", sizeof(client->name)); + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct cs53l32a_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &cs53l32a_ops); + + for (i = 1; i <= 7; i++) { + u8 v = cs53l32a_read(sd, i); + + v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v); + } + + v4l2_ctrl_handler_init(&state->hdl, 2); + v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, -96, 12, 1, 0); + v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + sd->ctrl_handler = &state->hdl; + if (state->hdl.error) { + int err = state->hdl.error; + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + + /* Set cs53l32a internal register for Adaptec 2010/2410 setup */ + + cs53l32a_write(sd, 0x01, 0x21); + cs53l32a_write(sd, 0x02, 0x29); + cs53l32a_write(sd, 0x03, 0x30); + cs53l32a_write(sd, 0x04, 0x00); + cs53l32a_write(sd, 0x05, 0x00); + cs53l32a_write(sd, 0x06, 0x00); + cs53l32a_write(sd, 0x07, 0x00); + + /* Display results, should be 0x21,0x29,0x30,0x00,0x00,0x00,0x00 */ + + for (i = 1; i <= 7; i++) { + u8 v = cs53l32a_read(sd, i); + + v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v); + } + return 0; +} + +static int cs53l32a_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct cs53l32a_state *state = to_state(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return 0; +} + +static const struct i2c_device_id cs53l32a_id[] = { + { "cs53l32a", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cs53l32a_id); + +static struct i2c_driver cs53l32a_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "cs53l32a", + }, + .probe = cs53l32a_probe, + .remove = cs53l32a_remove, + .id_table = cs53l32a_id, +}; + +module_i2c_driver(cs53l32a_driver); diff --git a/drivers/media/i2c/cx2341x.c b/drivers/media/i2c/cx2341x.c new file mode 100644 index 00000000000..103ef6bad2e --- /dev/null +++ b/drivers/media/i2c/cx2341x.c @@ -0,0 +1,1726 @@ +/* + * cx2341x - generic code for cx23415/6/8 based devices + * + * Copyright (C) 2006 Hans Verkuil + * + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +MODULE_DESCRIPTION("cx23415/6/8 driver"); +MODULE_AUTHOR("Hans Verkuil"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +/********************** COMMON CODE *********************/ + +/* definitions for audio properties bits 29-28 */ +#define CX2341X_AUDIO_ENCODING_METHOD_MPEG 0 +#define CX2341X_AUDIO_ENCODING_METHOD_AC3 1 +#define CX2341X_AUDIO_ENCODING_METHOD_LPCM 2 + +static const char *cx2341x_get_name(u32 id) +{ + switch (id) { + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + return "Spatial Filter Mode"; + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: + return "Spatial Filter"; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + return "Spatial Luma Filter Type"; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + return "Spatial Chroma Filter Type"; + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + return "Temporal Filter Mode"; + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: + return "Temporal Filter"; + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + return "Median Filter Type"; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: + return "Median Luma Filter Maximum"; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: + return "Median Luma Filter Minimum"; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: + return "Median Chroma Filter Maximum"; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: + return "Median Chroma Filter Minimum"; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + return "Insert Navigation Packets"; + } + return NULL; +} + +static const char **cx2341x_get_menu(u32 id) +{ + static const char *cx2341x_video_spatial_filter_mode_menu[] = { + "Manual", + "Auto", + NULL + }; + + static const char *cx2341x_video_luma_spatial_filter_type_menu[] = { + "Off", + "1D Horizontal", + "1D Vertical", + "2D H/V Separable", + "2D Symmetric non-separable", + NULL + }; + + static const char *cx2341x_video_chroma_spatial_filter_type_menu[] = { + "Off", + "1D Horizontal", + NULL + }; + + static const char *cx2341x_video_temporal_filter_mode_menu[] = { + "Manual", + "Auto", + NULL + }; + + static const char *cx2341x_video_median_filter_type_menu[] = { + "Off", + "Horizontal", + "Vertical", + "Horizontal/Vertical", + "Diagonal", + NULL + }; + + switch (id) { + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + return cx2341x_video_spatial_filter_mode_menu; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + return cx2341x_video_luma_spatial_filter_type_menu; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + return cx2341x_video_chroma_spatial_filter_type_menu; + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + return cx2341x_video_temporal_filter_mode_menu; + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + return cx2341x_video_median_filter_type_menu; + } + return NULL; +} + +static void cx2341x_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, + s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags) +{ + *name = cx2341x_get_name(id); + *flags = 0; + + switch (id) { + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + *type = V4L2_CTRL_TYPE_MENU; + *min = 0; + *step = 0; + break; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + *type = V4L2_CTRL_TYPE_BOOLEAN; + *min = 0; + *max = *step = 1; + break; + default: + *type = V4L2_CTRL_TYPE_INTEGER; + break; + } + switch (id) { + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + *flags |= V4L2_CTRL_FLAG_UPDATE; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: + *flags |= V4L2_CTRL_FLAG_SLIDER; + break; + case V4L2_CID_MPEG_VIDEO_ENCODING: + *flags |= V4L2_CTRL_FLAG_READ_ONLY; + break; + } +} + + +/********************** OLD CODE *********************/ + +/* Must be sorted from low to high control ID! */ +const u32 cx2341x_mpeg_ctrls[] = { + V4L2_CID_MPEG_CLASS, + V4L2_CID_MPEG_STREAM_TYPE, + V4L2_CID_MPEG_STREAM_VBI_FMT, + V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, + V4L2_CID_MPEG_AUDIO_ENCODING, + V4L2_CID_MPEG_AUDIO_L2_BITRATE, + V4L2_CID_MPEG_AUDIO_MODE, + V4L2_CID_MPEG_AUDIO_MODE_EXTENSION, + V4L2_CID_MPEG_AUDIO_EMPHASIS, + V4L2_CID_MPEG_AUDIO_CRC, + V4L2_CID_MPEG_AUDIO_MUTE, + V4L2_CID_MPEG_AUDIO_AC3_BITRATE, + V4L2_CID_MPEG_VIDEO_ENCODING, + V4L2_CID_MPEG_VIDEO_ASPECT, + V4L2_CID_MPEG_VIDEO_B_FRAMES, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, + V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + V4L2_CID_MPEG_VIDEO_BITRATE, + V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, + V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION, + V4L2_CID_MPEG_VIDEO_MUTE, + V4L2_CID_MPEG_VIDEO_MUTE_YUV, + V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE, + V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER, + V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE, + V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE, + V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE, + V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER, + V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE, + V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM, + V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP, + V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM, + V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP, + V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS, + 0 +}; +EXPORT_SYMBOL(cx2341x_mpeg_ctrls); + +static const struct cx2341x_mpeg_params default_params = { + /* misc */ + .capabilities = 0, + .port = CX2341X_PORT_MEMORY, + .width = 720, + .height = 480, + .is_50hz = 0, + + /* stream */ + .stream_type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS, + .stream_vbi_fmt = V4L2_MPEG_STREAM_VBI_FMT_NONE, + .stream_insert_nav_packets = 0, + + /* audio */ + .audio_sampling_freq = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, + .audio_encoding = V4L2_MPEG_AUDIO_ENCODING_LAYER_2, + .audio_l2_bitrate = V4L2_MPEG_AUDIO_L2_BITRATE_224K, + .audio_ac3_bitrate = V4L2_MPEG_AUDIO_AC3_BITRATE_224K, + .audio_mode = V4L2_MPEG_AUDIO_MODE_STEREO, + .audio_mode_extension = V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4, + .audio_emphasis = V4L2_MPEG_AUDIO_EMPHASIS_NONE, + .audio_crc = V4L2_MPEG_AUDIO_CRC_NONE, + .audio_mute = 0, + + /* video */ + .video_encoding = V4L2_MPEG_VIDEO_ENCODING_MPEG_2, + .video_aspect = V4L2_MPEG_VIDEO_ASPECT_4x3, + .video_b_frames = 2, + .video_gop_size = 12, + .video_gop_closure = 1, + .video_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, + .video_bitrate = 6000000, + .video_bitrate_peak = 8000000, + .video_temporal_decimation = 0, + .video_mute = 0, + .video_mute_yuv = 0x008080, /* YCbCr value for black */ + + /* encoding filters */ + .video_spatial_filter_mode = + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, + .video_spatial_filter = 0, + .video_luma_spatial_filter_type = + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR, + .video_chroma_spatial_filter_type = + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, + .video_temporal_filter_mode = + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL, + .video_temporal_filter = 8, + .video_median_filter_type = + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF, + .video_luma_median_filter_top = 255, + .video_luma_median_filter_bottom = 0, + .video_chroma_median_filter_top = 255, + .video_chroma_median_filter_bottom = 0, +}; +/* Map the control ID to the correct field in the cx2341x_mpeg_params + struct. Return -EINVAL if the ID is unknown, else return 0. */ +static int cx2341x_get_ctrl(const struct cx2341x_mpeg_params *params, + struct v4l2_ext_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: + ctrl->value = params->audio_sampling_freq; + break; + case V4L2_CID_MPEG_AUDIO_ENCODING: + ctrl->value = params->audio_encoding; + break; + case V4L2_CID_MPEG_AUDIO_L2_BITRATE: + ctrl->value = params->audio_l2_bitrate; + break; + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + ctrl->value = params->audio_ac3_bitrate; + break; + case V4L2_CID_MPEG_AUDIO_MODE: + ctrl->value = params->audio_mode; + break; + case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: + ctrl->value = params->audio_mode_extension; + break; + case V4L2_CID_MPEG_AUDIO_EMPHASIS: + ctrl->value = params->audio_emphasis; + break; + case V4L2_CID_MPEG_AUDIO_CRC: + ctrl->value = params->audio_crc; + break; + case V4L2_CID_MPEG_AUDIO_MUTE: + ctrl->value = params->audio_mute; + break; + case V4L2_CID_MPEG_VIDEO_ENCODING: + ctrl->value = params->video_encoding; + break; + case V4L2_CID_MPEG_VIDEO_ASPECT: + ctrl->value = params->video_aspect; + break; + case V4L2_CID_MPEG_VIDEO_B_FRAMES: + ctrl->value = params->video_b_frames; + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + ctrl->value = params->video_gop_size; + break; + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: + ctrl->value = params->video_gop_closure; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + ctrl->value = params->video_bitrate_mode; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + ctrl->value = params->video_bitrate; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + ctrl->value = params->video_bitrate_peak; + break; + case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: + ctrl->value = params->video_temporal_decimation; + break; + case V4L2_CID_MPEG_VIDEO_MUTE: + ctrl->value = params->video_mute; + break; + case V4L2_CID_MPEG_VIDEO_MUTE_YUV: + ctrl->value = params->video_mute_yuv; + break; + case V4L2_CID_MPEG_STREAM_TYPE: + ctrl->value = params->stream_type; + break; + case V4L2_CID_MPEG_STREAM_VBI_FMT: + ctrl->value = params->stream_vbi_fmt; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + ctrl->value = params->video_spatial_filter_mode; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: + ctrl->value = params->video_spatial_filter; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + ctrl->value = params->video_luma_spatial_filter_type; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + ctrl->value = params->video_chroma_spatial_filter_type; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + ctrl->value = params->video_temporal_filter_mode; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: + ctrl->value = params->video_temporal_filter; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + ctrl->value = params->video_median_filter_type; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: + ctrl->value = params->video_luma_median_filter_top; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: + ctrl->value = params->video_luma_median_filter_bottom; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: + ctrl->value = params->video_chroma_median_filter_top; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: + ctrl->value = params->video_chroma_median_filter_bottom; + break; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + ctrl->value = params->stream_insert_nav_packets; + break; + default: + return -EINVAL; + } + return 0; +} + +/* Map the control ID to the correct field in the cx2341x_mpeg_params + struct. Return -EINVAL if the ID is unknown, else return 0. */ +static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params, int busy, + struct v4l2_ext_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: + if (busy) + return -EBUSY; + params->audio_sampling_freq = ctrl->value; + break; + case V4L2_CID_MPEG_AUDIO_ENCODING: + if (busy) + return -EBUSY; + if (params->capabilities & CX2341X_CAP_HAS_AC3) + if (ctrl->value != V4L2_MPEG_AUDIO_ENCODING_LAYER_2 && + ctrl->value != V4L2_MPEG_AUDIO_ENCODING_AC3) + return -ERANGE; + params->audio_encoding = ctrl->value; + break; + case V4L2_CID_MPEG_AUDIO_L2_BITRATE: + if (busy) + return -EBUSY; + params->audio_l2_bitrate = ctrl->value; + break; + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + if (busy) + return -EBUSY; + if (!(params->capabilities & CX2341X_CAP_HAS_AC3)) + return -EINVAL; + params->audio_ac3_bitrate = ctrl->value; + break; + case V4L2_CID_MPEG_AUDIO_MODE: + params->audio_mode = ctrl->value; + break; + case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: + params->audio_mode_extension = ctrl->value; + break; + case V4L2_CID_MPEG_AUDIO_EMPHASIS: + params->audio_emphasis = ctrl->value; + break; + case V4L2_CID_MPEG_AUDIO_CRC: + params->audio_crc = ctrl->value; + break; + case V4L2_CID_MPEG_AUDIO_MUTE: + params->audio_mute = ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_ASPECT: + params->video_aspect = ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_B_FRAMES: { + int b = ctrl->value + 1; + int gop = params->video_gop_size; + params->video_b_frames = ctrl->value; + params->video_gop_size = b * ((gop + b - 1) / b); + /* Max GOP size = 34 */ + while (params->video_gop_size > 34) + params->video_gop_size -= b; + break; + } + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: { + int b = params->video_b_frames + 1; + int gop = ctrl->value; + params->video_gop_size = b * ((gop + b - 1) / b); + /* Max GOP size = 34 */ + while (params->video_gop_size > 34) + params->video_gop_size -= b; + ctrl->value = params->video_gop_size; + break; + } + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: + params->video_gop_closure = ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + if (busy) + return -EBUSY; + /* MPEG-1 only allows CBR */ + if (params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1 && + ctrl->value != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) + return -EINVAL; + params->video_bitrate_mode = ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + if (busy) + return -EBUSY; + params->video_bitrate = ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + if (busy) + return -EBUSY; + params->video_bitrate_peak = ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: + params->video_temporal_decimation = ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_MUTE: + params->video_mute = (ctrl->value != 0); + break; + case V4L2_CID_MPEG_VIDEO_MUTE_YUV: + params->video_mute_yuv = ctrl->value; + break; + case V4L2_CID_MPEG_STREAM_TYPE: + if (busy) + return -EBUSY; + params->stream_type = ctrl->value; + params->video_encoding = + (params->stream_type == V4L2_MPEG_STREAM_TYPE_MPEG1_SS || + params->stream_type == V4L2_MPEG_STREAM_TYPE_MPEG1_VCD) ? + V4L2_MPEG_VIDEO_ENCODING_MPEG_1 : + V4L2_MPEG_VIDEO_ENCODING_MPEG_2; + if (params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) + /* MPEG-1 implies CBR */ + params->video_bitrate_mode = + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR; + break; + case V4L2_CID_MPEG_STREAM_VBI_FMT: + params->stream_vbi_fmt = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + params->video_spatial_filter_mode = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: + params->video_spatial_filter = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + params->video_luma_spatial_filter_type = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + params->video_chroma_spatial_filter_type = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + params->video_temporal_filter_mode = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: + params->video_temporal_filter = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + params->video_median_filter_type = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: + params->video_luma_median_filter_top = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: + params->video_luma_median_filter_bottom = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: + params->video_chroma_median_filter_top = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: + params->video_chroma_median_filter_bottom = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + params->stream_insert_nav_packets = ctrl->value; + break; + default: + return -EINVAL; + } + return 0; +} + +static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl, + s32 min, s32 max, s32 step, s32 def) +{ + const char *name; + + switch (qctrl->id) { + /* MPEG controls */ + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + cx2341x_ctrl_fill(qctrl->id, &name, &qctrl->type, + &min, &max, &step, &def, &qctrl->flags); + qctrl->minimum = min; + qctrl->maximum = max; + qctrl->step = step; + qctrl->default_value = def; + qctrl->reserved[0] = qctrl->reserved[1] = 0; + strlcpy(qctrl->name, name, sizeof(qctrl->name)); + return 0; + + default: + return v4l2_ctrl_query_fill(qctrl, min, max, step, def); + } +} + +int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params, + struct v4l2_queryctrl *qctrl) +{ + int err; + + switch (qctrl->id) { + case V4L2_CID_MPEG_CLASS: + return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0); + case V4L2_CID_MPEG_STREAM_TYPE: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_STREAM_TYPE_MPEG2_PS, + V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, 1, + V4L2_MPEG_STREAM_TYPE_MPEG2_PS); + + case V4L2_CID_MPEG_STREAM_VBI_FMT: + if (params->capabilities & CX2341X_CAP_HAS_SLICED_VBI) + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_STREAM_VBI_FMT_NONE, + V4L2_MPEG_STREAM_VBI_FMT_IVTV, 1, + V4L2_MPEG_STREAM_VBI_FMT_NONE); + return cx2341x_ctrl_query_fill(qctrl, + V4L2_MPEG_STREAM_VBI_FMT_NONE, + V4L2_MPEG_STREAM_VBI_FMT_NONE, 1, + default_params.stream_vbi_fmt); + + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, 1, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000); + + case V4L2_CID_MPEG_AUDIO_ENCODING: + if (params->capabilities & CX2341X_CAP_HAS_AC3) { + /* + * The state of L2 & AC3 bitrate controls can change + * when this control changes, but v4l2_ctrl_query_fill() + * already sets V4L2_CTRL_FLAG_UPDATE for + * V4L2_CID_MPEG_AUDIO_ENCODING, so we don't here. + */ + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_ENCODING_LAYER_2, + V4L2_MPEG_AUDIO_ENCODING_AC3, 1, + default_params.audio_encoding); + } + + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_ENCODING_LAYER_2, + V4L2_MPEG_AUDIO_ENCODING_LAYER_2, 1, + default_params.audio_encoding); + + case V4L2_CID_MPEG_AUDIO_L2_BITRATE: + err = v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_L2_BITRATE_192K, + V4L2_MPEG_AUDIO_L2_BITRATE_384K, 1, + default_params.audio_l2_bitrate); + if (err) + return err; + if (params->capabilities & CX2341X_CAP_HAS_AC3 && + params->audio_encoding != V4L2_MPEG_AUDIO_ENCODING_LAYER_2) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; + + case V4L2_CID_MPEG_AUDIO_MODE: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_MODE_STEREO, + V4L2_MPEG_AUDIO_MODE_MONO, 1, + V4L2_MPEG_AUDIO_MODE_STEREO); + + case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: + err = v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, 1, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4); + if (err == 0 && + params->audio_mode != V4L2_MPEG_AUDIO_MODE_JOINT_STEREO) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return err; + + case V4L2_CID_MPEG_AUDIO_EMPHASIS: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_EMPHASIS_NONE, + V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, 1, + V4L2_MPEG_AUDIO_EMPHASIS_NONE); + + case V4L2_CID_MPEG_AUDIO_CRC: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_CRC_NONE, + V4L2_MPEG_AUDIO_CRC_CRC16, 1, + V4L2_MPEG_AUDIO_CRC_NONE); + + case V4L2_CID_MPEG_AUDIO_MUTE: + return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); + + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + err = v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_AC3_BITRATE_48K, + V4L2_MPEG_AUDIO_AC3_BITRATE_448K, 1, + default_params.audio_ac3_bitrate); + if (err) + return err; + if (params->capabilities & CX2341X_CAP_HAS_AC3) { + if (params->audio_encoding != + V4L2_MPEG_AUDIO_ENCODING_AC3) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + } else + qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; + return 0; + + case V4L2_CID_MPEG_VIDEO_ENCODING: + /* this setting is read-only for the cx2341x since the + V4L2_CID_MPEG_STREAM_TYPE really determines the + MPEG-1/2 setting */ + err = v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_VIDEO_ENCODING_MPEG_1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2); + if (err == 0) + qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + return err; + + case V4L2_CID_MPEG_VIDEO_ASPECT: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_VIDEO_ASPECT_1x1, + V4L2_MPEG_VIDEO_ASPECT_221x100, 1, + V4L2_MPEG_VIDEO_ASPECT_4x3); + + case V4L2_CID_MPEG_VIDEO_B_FRAMES: + return v4l2_ctrl_query_fill(qctrl, 0, 33, 1, 2); + + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + return v4l2_ctrl_query_fill(qctrl, 1, 34, 1, + params->is_50hz ? 12 : 15); + + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: + return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 1); + + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + err = v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1, + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); + if (err == 0 && + params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return err; + + case V4L2_CID_MPEG_VIDEO_BITRATE: + return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 6000000); + + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + err = v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 8000000); + if (err == 0 && + params->video_bitrate_mode == + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return err; + + case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: + return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 0); + + case V4L2_CID_MPEG_VIDEO_MUTE: + return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); + + case V4L2_CID_MPEG_VIDEO_MUTE_YUV: /* Init YUV (really YCbCr) to black */ + return v4l2_ctrl_query_fill(qctrl, 0, 0xffffff, 1, 0x008080); + + /* CX23415/6 specific */ + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + return cx2341x_ctrl_query_fill(qctrl, + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 1, + default_params.video_spatial_filter_mode); + + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: + cx2341x_ctrl_query_fill(qctrl, 0, 15, 1, + default_params.video_spatial_filter); + qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; + if (params->video_spatial_filter_mode == + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; + + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + cx2341x_ctrl_query_fill(qctrl, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE, + 1, + default_params.video_luma_spatial_filter_type); + if (params->video_spatial_filter_mode == + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; + + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + cx2341x_ctrl_query_fill(qctrl, + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF, + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, + 1, + default_params.video_chroma_spatial_filter_type); + if (params->video_spatial_filter_mode == + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; + + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + return cx2341x_ctrl_query_fill(qctrl, + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL, + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO, 1, + default_params.video_temporal_filter_mode); + + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: + cx2341x_ctrl_query_fill(qctrl, 0, 31, 1, + default_params.video_temporal_filter); + qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; + if (params->video_temporal_filter_mode == + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; + + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + return cx2341x_ctrl_query_fill(qctrl, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG, 1, + default_params.video_median_filter_type); + + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: + cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, + default_params.video_luma_median_filter_top); + qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; + if (params->video_median_filter_type == + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; + + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: + cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, + default_params.video_luma_median_filter_bottom); + qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; + if (params->video_median_filter_type == + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; + + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: + cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, + default_params.video_chroma_median_filter_top); + qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; + if (params->video_median_filter_type == + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; + + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: + cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, + default_params.video_chroma_median_filter_bottom); + qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; + if (params->video_median_filter_type == + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; + + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + return cx2341x_ctrl_query_fill(qctrl, 0, 1, 1, + default_params.stream_insert_nav_packets); + + default: + return -EINVAL; + + } +} +EXPORT_SYMBOL(cx2341x_ctrl_query); + +const char * const *cx2341x_ctrl_get_menu(const struct cx2341x_mpeg_params *p, u32 id) +{ + static const char * const mpeg_stream_type_without_ts[] = { + "MPEG-2 Program Stream", + "", + "MPEG-1 System Stream", + "MPEG-2 DVD-compatible Stream", + "MPEG-1 VCD-compatible Stream", + "MPEG-2 SVCD-compatible Stream", + NULL + }; + + static const char *mpeg_stream_type_with_ts[] = { + "MPEG-2 Program Stream", + "MPEG-2 Transport Stream", + "MPEG-1 System Stream", + "MPEG-2 DVD-compatible Stream", + "MPEG-1 VCD-compatible Stream", + "MPEG-2 SVCD-compatible Stream", + NULL + }; + + static const char *mpeg_audio_encoding_l2_ac3[] = { + "", + "MPEG-1/2 Layer II", + "", + "", + "AC-3", + NULL + }; + + switch (id) { + case V4L2_CID_MPEG_STREAM_TYPE: + return (p->capabilities & CX2341X_CAP_HAS_TS) ? + mpeg_stream_type_with_ts : mpeg_stream_type_without_ts; + case V4L2_CID_MPEG_AUDIO_ENCODING: + return (p->capabilities & CX2341X_CAP_HAS_AC3) ? + mpeg_audio_encoding_l2_ac3 : v4l2_ctrl_get_menu(id); + case V4L2_CID_MPEG_AUDIO_L1_BITRATE: + case V4L2_CID_MPEG_AUDIO_L3_BITRATE: + return NULL; + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + return cx2341x_get_menu(id); + default: + return v4l2_ctrl_get_menu(id); + } +} +EXPORT_SYMBOL(cx2341x_ctrl_get_menu); + +static void cx2341x_calc_audio_properties(struct cx2341x_mpeg_params *params) +{ + params->audio_properties = + (params->audio_sampling_freq << 0) | + (params->audio_mode << 8) | + (params->audio_mode_extension << 10) | + (((params->audio_emphasis == V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17) + ? 3 : params->audio_emphasis) << 12) | + (params->audio_crc << 14); + + if ((params->capabilities & CX2341X_CAP_HAS_AC3) && + params->audio_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3) { + params->audio_properties |= + /* Not sure if this MPEG Layer II setting is required */ + ((3 - V4L2_MPEG_AUDIO_ENCODING_LAYER_2) << 2) | + (params->audio_ac3_bitrate << 4) | + (CX2341X_AUDIO_ENCODING_METHOD_AC3 << 28); + } else { + /* Assuming MPEG Layer II */ + params->audio_properties |= + ((3 - params->audio_encoding) << 2) | + ((1 + params->audio_l2_bitrate) << 4); + } +} + +int cx2341x_ext_ctrls(struct cx2341x_mpeg_params *params, int busy, + struct v4l2_ext_controls *ctrls, unsigned int cmd) +{ + int err = 0; + int i; + + if (cmd == VIDIOC_G_EXT_CTRLS) { + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl = ctrls->controls + i; + + err = cx2341x_get_ctrl(params, ctrl); + if (err) { + ctrls->error_idx = i; + break; + } + } + return err; + } + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl = ctrls->controls + i; + struct v4l2_queryctrl qctrl; + const char * const *menu_items = NULL; + + qctrl.id = ctrl->id; + err = cx2341x_ctrl_query(params, &qctrl); + if (err) + break; + if (qctrl.type == V4L2_CTRL_TYPE_MENU) + menu_items = cx2341x_ctrl_get_menu(params, qctrl.id); + err = v4l2_ctrl_check(ctrl, &qctrl, menu_items); + if (err) + break; + err = cx2341x_set_ctrl(params, busy, ctrl); + if (err) + break; + } + if (err == 0 && + params->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && + params->video_bitrate_peak < params->video_bitrate) { + err = -ERANGE; + ctrls->error_idx = ctrls->count; + } + if (err) + ctrls->error_idx = i; + else + cx2341x_calc_audio_properties(params); + return err; +} +EXPORT_SYMBOL(cx2341x_ext_ctrls); + +void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p) +{ + *p = default_params; + cx2341x_calc_audio_properties(p); +} +EXPORT_SYMBOL(cx2341x_fill_defaults); + +static int cx2341x_api(void *priv, cx2341x_mbox_func func, + u32 cmd, int args, ...) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + va_list vargs; + int i; + + va_start(vargs, args); + + for (i = 0; i < args; i++) + data[i] = va_arg(vargs, int); + va_end(vargs); + return func(priv, cmd, args, 0, data); +} + +#define NEQ(field) (old->field != new->field) + +int cx2341x_update(void *priv, cx2341x_mbox_func func, + const struct cx2341x_mpeg_params *old, + const struct cx2341x_mpeg_params *new) +{ + static int mpeg_stream_type[] = { + 0, /* MPEG-2 PS */ + 1, /* MPEG-2 TS */ + 2, /* MPEG-1 SS */ + 14, /* DVD */ + 11, /* VCD */ + 12, /* SVCD */ + }; + + int err = 0; + int force = (old == NULL); + u16 temporal = new->video_temporal_filter; + + cx2341x_api(priv, func, CX2341X_ENC_SET_OUTPUT_PORT, 2, new->port, 0); + + if (force || NEQ(is_50hz)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_RATE, 1, + new->is_50hz); + if (err) return err; + } + + if (force || NEQ(width) || NEQ(height) || NEQ(video_encoding)) { + u16 w = new->width; + u16 h = new->height; + + if (new->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) { + w /= 2; + h /= 2; + } + err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_SIZE, 2, + h, w); + if (err) return err; + } + if (force || NEQ(stream_type)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_STREAM_TYPE, 1, + mpeg_stream_type[new->stream_type]); + if (err) return err; + } + if (force || NEQ(video_aspect)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_ASPECT_RATIO, 1, + 1 + new->video_aspect); + if (err) return err; + } + if (force || NEQ(video_b_frames) || NEQ(video_gop_size)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_GOP_PROPERTIES, 2, + new->video_gop_size, new->video_b_frames + 1); + if (err) return err; + } + if (force || NEQ(video_gop_closure)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_GOP_CLOSURE, 1, + new->video_gop_closure); + if (err) return err; + } + if (force || NEQ(audio_properties)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_AUDIO_PROPERTIES, + 1, new->audio_properties); + if (err) return err; + } + if (force || NEQ(audio_mute)) { + err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_AUDIO, 1, + new->audio_mute); + if (err) return err; + } + if (force || NEQ(video_bitrate_mode) || NEQ(video_bitrate) || + NEQ(video_bitrate_peak)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_BIT_RATE, 5, + new->video_bitrate_mode, new->video_bitrate, + new->video_bitrate_peak / 400, 0, 0); + if (err) return err; + } + if (force || NEQ(video_spatial_filter_mode) || + NEQ(video_temporal_filter_mode) || + NEQ(video_median_filter_type)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_MODE, + 2, new->video_spatial_filter_mode | + (new->video_temporal_filter_mode << 1), + new->video_median_filter_type); + if (err) return err; + } + if (force || NEQ(video_luma_median_filter_bottom) || + NEQ(video_luma_median_filter_top) || + NEQ(video_chroma_median_filter_bottom) || + NEQ(video_chroma_median_filter_top)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_CORING_LEVELS, 4, + new->video_luma_median_filter_bottom, + new->video_luma_median_filter_top, + new->video_chroma_median_filter_bottom, + new->video_chroma_median_filter_top); + if (err) return err; + } + if (force || NEQ(video_luma_spatial_filter_type) || + NEQ(video_chroma_spatial_filter_type)) { + err = cx2341x_api(priv, func, + CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, + 2, new->video_luma_spatial_filter_type, + new->video_chroma_spatial_filter_type); + if (err) return err; + } + if (force || NEQ(video_spatial_filter) || + old->video_temporal_filter != temporal) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_PROPS, + 2, new->video_spatial_filter, temporal); + if (err) return err; + } + if (force || NEQ(video_temporal_decimation)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_DROP_RATE, + 1, new->video_temporal_decimation); + if (err) return err; + } + if (force || NEQ(video_mute) || + (new->video_mute && NEQ(video_mute_yuv))) { + err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_VIDEO, 1, + new->video_mute | (new->video_mute_yuv << 8)); + if (err) return err; + } + if (force || NEQ(stream_insert_nav_packets)) { + err = cx2341x_api(priv, func, CX2341X_ENC_MISC, 2, + 7, new->stream_insert_nav_packets); + if (err) return err; + } + return 0; +} +EXPORT_SYMBOL(cx2341x_update); + +static const char *cx2341x_menu_item(const struct cx2341x_mpeg_params *p, u32 id) +{ + const char * const *menu = cx2341x_ctrl_get_menu(p, id); + struct v4l2_ext_control ctrl; + + if (menu == NULL) + goto invalid; + ctrl.id = id; + if (cx2341x_get_ctrl(p, &ctrl)) + goto invalid; + while (ctrl.value-- && *menu) menu++; + if (*menu == NULL) + goto invalid; + return *menu; + +invalid: + return ""; +} + +void cx2341x_log_status(const struct cx2341x_mpeg_params *p, const char *prefix) +{ + int is_mpeg1 = p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1; + + /* Stream */ + printk(KERN_INFO "%s: Stream: %s", + prefix, + cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_TYPE)); + if (p->stream_insert_nav_packets) + printk(" (with navigation packets)"); + printk("\n"); + printk(KERN_INFO "%s: VBI Format: %s\n", + prefix, + cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_VBI_FMT)); + + /* Video */ + printk(KERN_INFO "%s: Video: %dx%d, %d fps%s\n", + prefix, + p->width / (is_mpeg1 ? 2 : 1), p->height / (is_mpeg1 ? 2 : 1), + p->is_50hz ? 25 : 30, + (p->video_mute) ? " (muted)" : ""); + printk(KERN_INFO "%s: Video: %s, %s, %s, %d", + prefix, + cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ENCODING), + cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ASPECT), + cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_BITRATE_MODE), + p->video_bitrate); + if (p->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) + printk(", Peak %d", p->video_bitrate_peak); + printk("\n"); + printk(KERN_INFO + "%s: Video: GOP Size %d, %d B-Frames, %sGOP Closure\n", + prefix, + p->video_gop_size, p->video_b_frames, + p->video_gop_closure ? "" : "No "); + if (p->video_temporal_decimation) + printk(KERN_INFO "%s: Video: Temporal Decimation %d\n", + prefix, p->video_temporal_decimation); + + /* Audio */ + printk(KERN_INFO "%s: Audio: %s, %s, %s, %s%s", + prefix, + cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ), + cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_ENCODING), + cx2341x_menu_item(p, + p->audio_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3 + ? V4L2_CID_MPEG_AUDIO_AC3_BITRATE + : V4L2_CID_MPEG_AUDIO_L2_BITRATE), + cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE), + p->audio_mute ? " (muted)" : ""); + if (p->audio_mode == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO) + printk(", %s", cx2341x_menu_item(p, + V4L2_CID_MPEG_AUDIO_MODE_EXTENSION)); + printk(", %s, %s\n", + cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_EMPHASIS), + cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_CRC)); + + /* Encoding filters */ + printk(KERN_INFO "%s: Spatial Filter: %s, Luma %s, Chroma %s, %d\n", + prefix, + cx2341x_menu_item(p, + V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE), + cx2341x_menu_item(p, + V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE), + cx2341x_menu_item(p, + V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE), + p->video_spatial_filter); + + printk(KERN_INFO "%s: Temporal Filter: %s, %d\n", + prefix, + cx2341x_menu_item(p, + V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE), + p->video_temporal_filter); + printk(KERN_INFO + "%s: Median Filter: %s, Luma [%d, %d], Chroma [%d, %d]\n", + prefix, + cx2341x_menu_item(p, + V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE), + p->video_luma_median_filter_bottom, + p->video_luma_median_filter_top, + p->video_chroma_median_filter_bottom, + p->video_chroma_median_filter_top); +} +EXPORT_SYMBOL(cx2341x_log_status); + + + +/********************** NEW CODE *********************/ + +static inline struct cx2341x_handler *to_cxhdl(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct cx2341x_handler, hdl); +} + +static int cx2341x_hdl_api(struct cx2341x_handler *hdl, + u32 cmd, int args, ...) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + va_list vargs; + int i; + + va_start(vargs, args); + + for (i = 0; i < args; i++) + data[i] = va_arg(vargs, int); + va_end(vargs); + return hdl->func(hdl->priv, cmd, args, 0, data); +} + +/* ctrl->handler->lock is held, so it is safe to access cur.val */ +static inline int cx2341x_neq(struct v4l2_ctrl *ctrl) +{ + return ctrl && ctrl->val != ctrl->cur.val; +} + +static int cx2341x_try_ctrl(struct v4l2_ctrl *ctrl) +{ + struct cx2341x_handler *hdl = to_cxhdl(ctrl); + s32 val = ctrl->val; + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_B_FRAMES: { + /* video gop cluster */ + int b = val + 1; + int gop = hdl->video_gop_size->val; + + gop = b * ((gop + b - 1) / b); + + /* Max GOP size = 34 */ + while (gop > 34) + gop -= b; + hdl->video_gop_size->val = gop; + break; + } + + case V4L2_CID_MPEG_STREAM_TYPE: + /* stream type cluster */ + hdl->video_encoding->val = + (hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_SS || + hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_VCD) ? + V4L2_MPEG_VIDEO_ENCODING_MPEG_1 : + V4L2_MPEG_VIDEO_ENCODING_MPEG_2; + if (hdl->video_encoding->val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) + /* MPEG-1 implies CBR */ + hdl->video_bitrate_mode->val = + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR; + /* peak bitrate shall be >= normal bitrate */ + if (hdl->video_bitrate_mode->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && + hdl->video_bitrate_peak->val < hdl->video_bitrate->val) + hdl->video_bitrate_peak->val = hdl->video_bitrate->val; + break; + } + return 0; +} + +static int cx2341x_s_ctrl(struct v4l2_ctrl *ctrl) +{ + static const int mpeg_stream_type[] = { + 0, /* MPEG-2 PS */ + 1, /* MPEG-2 TS */ + 2, /* MPEG-1 SS */ + 14, /* DVD */ + 11, /* VCD */ + 12, /* SVCD */ + }; + struct cx2341x_handler *hdl = to_cxhdl(ctrl); + s32 val = ctrl->val; + u32 props; + int err; + + switch (ctrl->id) { + case V4L2_CID_MPEG_STREAM_VBI_FMT: + if (hdl->ops && hdl->ops->s_stream_vbi_fmt) + return hdl->ops->s_stream_vbi_fmt(hdl, val); + return 0; + + case V4L2_CID_MPEG_VIDEO_ASPECT: + return cx2341x_hdl_api(hdl, + CX2341X_ENC_SET_ASPECT_RATIO, 1, val + 1); + + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: + return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_CLOSURE, 1, val); + + case V4L2_CID_MPEG_AUDIO_MUTE: + return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_AUDIO, 1, val); + + case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: + return cx2341x_hdl_api(hdl, + CX2341X_ENC_SET_FRAME_DROP_RATE, 1, val); + + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + return cx2341x_hdl_api(hdl, CX2341X_ENC_MISC, 2, 7, val); + + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: + /* audio properties cluster */ + props = (hdl->audio_sampling_freq->val << 0) | + (hdl->audio_mode->val << 8) | + (hdl->audio_mode_extension->val << 10) | + (hdl->audio_crc->val << 14); + if (hdl->audio_emphasis->val == V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17) + props |= 3 << 12; + else + props |= hdl->audio_emphasis->val << 12; + + if (hdl->audio_encoding->val == V4L2_MPEG_AUDIO_ENCODING_AC3) { + props |= +#if 1 + /* Not sure if this MPEG Layer II setting is required */ + ((3 - V4L2_MPEG_AUDIO_ENCODING_LAYER_2) << 2) | +#endif + (hdl->audio_ac3_bitrate->val << 4) | + (CX2341X_AUDIO_ENCODING_METHOD_AC3 << 28); + } else { + /* Assuming MPEG Layer II */ + props |= + ((3 - hdl->audio_encoding->val) << 2) | + ((1 + hdl->audio_l2_bitrate->val) << 4); + } + err = cx2341x_hdl_api(hdl, + CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, props); + if (err) + return err; + + hdl->audio_properties = props; + if (hdl->audio_ac3_bitrate) { + int is_ac3 = hdl->audio_encoding->val == + V4L2_MPEG_AUDIO_ENCODING_AC3; + + v4l2_ctrl_activate(hdl->audio_ac3_bitrate, is_ac3); + v4l2_ctrl_activate(hdl->audio_l2_bitrate, !is_ac3); + } + v4l2_ctrl_activate(hdl->audio_mode_extension, + hdl->audio_mode->val == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO); + if (cx2341x_neq(hdl->audio_sampling_freq) && + hdl->ops && hdl->ops->s_audio_sampling_freq) + return hdl->ops->s_audio_sampling_freq(hdl, hdl->audio_sampling_freq->val); + if (cx2341x_neq(hdl->audio_mode) && + hdl->ops && hdl->ops->s_audio_mode) + return hdl->ops->s_audio_mode(hdl, hdl->audio_mode->val); + return 0; + + case V4L2_CID_MPEG_VIDEO_B_FRAMES: + /* video gop cluster */ + return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_PROPERTIES, 2, + hdl->video_gop_size->val, + hdl->video_b_frames->val + 1); + + case V4L2_CID_MPEG_STREAM_TYPE: + /* stream type cluster */ + err = cx2341x_hdl_api(hdl, + CX2341X_ENC_SET_STREAM_TYPE, 1, mpeg_stream_type[val]); + if (err) + return err; + + err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_BIT_RATE, 5, + hdl->video_bitrate_mode->val, + hdl->video_bitrate->val, + hdl->video_bitrate_peak->val / 400, 0, 0); + if (err) + return err; + + v4l2_ctrl_activate(hdl->video_bitrate_mode, + hdl->video_encoding->val != V4L2_MPEG_VIDEO_ENCODING_MPEG_1); + v4l2_ctrl_activate(hdl->video_bitrate_peak, + hdl->video_bitrate_mode->val != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); + if (cx2341x_neq(hdl->video_encoding) && + hdl->ops && hdl->ops->s_video_encoding) + return hdl->ops->s_video_encoding(hdl, hdl->video_encoding->val); + return 0; + + case V4L2_CID_MPEG_VIDEO_MUTE: + /* video mute cluster */ + return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_VIDEO, 1, + hdl->video_mute->val | + (hdl->video_mute_yuv->val << 8)); + + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: { + int active_filter; + + /* video filter mode */ + err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_MODE, 2, + hdl->video_spatial_filter_mode->val | + (hdl->video_temporal_filter_mode->val << 1), + hdl->video_median_filter_type->val); + if (err) + return err; + + active_filter = hdl->video_spatial_filter_mode->val != + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO; + v4l2_ctrl_activate(hdl->video_spatial_filter, active_filter); + v4l2_ctrl_activate(hdl->video_luma_spatial_filter_type, active_filter); + v4l2_ctrl_activate(hdl->video_chroma_spatial_filter_type, active_filter); + active_filter = hdl->video_temporal_filter_mode->val != + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO; + v4l2_ctrl_activate(hdl->video_temporal_filter, active_filter); + active_filter = hdl->video_median_filter_type->val != + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF; + v4l2_ctrl_activate(hdl->video_luma_median_filter_bottom, active_filter); + v4l2_ctrl_activate(hdl->video_luma_median_filter_top, active_filter); + v4l2_ctrl_activate(hdl->video_chroma_median_filter_bottom, active_filter); + v4l2_ctrl_activate(hdl->video_chroma_median_filter_top, active_filter); + return 0; + } + + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + /* video filter type cluster */ + return cx2341x_hdl_api(hdl, + CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, 2, + hdl->video_luma_spatial_filter_type->val, + hdl->video_chroma_spatial_filter_type->val); + + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: + /* video filter cluster */ + return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_PROPS, 2, + hdl->video_spatial_filter->val, + hdl->video_temporal_filter->val); + + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: + /* video median cluster */ + return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_CORING_LEVELS, 4, + hdl->video_luma_median_filter_bottom->val, + hdl->video_luma_median_filter_top->val, + hdl->video_chroma_median_filter_bottom->val, + hdl->video_chroma_median_filter_top->val); + } + return -EINVAL; +} + +static const struct v4l2_ctrl_ops cx2341x_ops = { + .try_ctrl = cx2341x_try_ctrl, + .s_ctrl = cx2341x_s_ctrl, +}; + +static struct v4l2_ctrl *cx2341x_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, + u32 id, s32 min, s32 max, s32 step, s32 def) +{ + struct v4l2_ctrl_config cfg; + + cx2341x_ctrl_fill(id, &cfg.name, &cfg.type, &min, &max, &step, &def, &cfg.flags); + cfg.ops = &cx2341x_ops; + cfg.id = id; + cfg.min = min; + cfg.max = max; + cfg.def = def; + if (cfg.type == V4L2_CTRL_TYPE_MENU) { + cfg.step = 0; + cfg.menu_skip_mask = step; + cfg.qmenu = cx2341x_get_menu(id); + } else { + cfg.step = step; + cfg.menu_skip_mask = 0; + } + return v4l2_ctrl_new_custom(hdl, &cfg, NULL); +} + +static struct v4l2_ctrl *cx2341x_ctrl_new_std(struct v4l2_ctrl_handler *hdl, + u32 id, s32 min, s32 max, s32 step, s32 def) +{ + return v4l2_ctrl_new_std(hdl, &cx2341x_ops, id, min, max, step, def); +} + +static struct v4l2_ctrl *cx2341x_ctrl_new_menu(struct v4l2_ctrl_handler *hdl, + u32 id, s32 max, s32 mask, s32 def) +{ + return v4l2_ctrl_new_std_menu(hdl, &cx2341x_ops, id, max, mask, def); +} + +int cx2341x_handler_init(struct cx2341x_handler *cxhdl, + unsigned nr_of_controls_hint) +{ + struct v4l2_ctrl_handler *hdl = &cxhdl->hdl; + u32 caps = cxhdl->capabilities; + int has_sliced_vbi = caps & CX2341X_CAP_HAS_SLICED_VBI; + int has_ac3 = caps & CX2341X_CAP_HAS_AC3; + int has_ts = caps & CX2341X_CAP_HAS_TS; + + cxhdl->width = 720; + cxhdl->height = 480; + + v4l2_ctrl_handler_init(hdl, nr_of_controls_hint); + + /* Add controls in ascending control ID order for fastest + insertion time. */ + cxhdl->stream_type = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_STREAM_TYPE, + V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, has_ts ? 0 : 2, + V4L2_MPEG_STREAM_TYPE_MPEG2_PS); + cxhdl->stream_vbi_fmt = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_STREAM_VBI_FMT, + V4L2_MPEG_STREAM_VBI_FMT_IVTV, has_sliced_vbi ? 0 : 2, + V4L2_MPEG_STREAM_VBI_FMT_NONE); + cxhdl->audio_sampling_freq = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, 0, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000); + cxhdl->audio_encoding = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_ENCODING, + V4L2_MPEG_AUDIO_ENCODING_AC3, has_ac3 ? ~0x12 : ~0x2, + V4L2_MPEG_AUDIO_ENCODING_LAYER_2); + cxhdl->audio_l2_bitrate = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_L2_BITRATE, + V4L2_MPEG_AUDIO_L2_BITRATE_384K, 0x1ff, + V4L2_MPEG_AUDIO_L2_BITRATE_224K); + cxhdl->audio_mode = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_MODE, + V4L2_MPEG_AUDIO_MODE_MONO, 0, + V4L2_MPEG_AUDIO_MODE_STEREO); + cxhdl->audio_mode_extension = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_MODE_EXTENSION, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, 0, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4); + cxhdl->audio_emphasis = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_EMPHASIS, + V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, 0, + V4L2_MPEG_AUDIO_EMPHASIS_NONE); + cxhdl->audio_crc = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_CRC, + V4L2_MPEG_AUDIO_CRC_CRC16, 0, + V4L2_MPEG_AUDIO_CRC_NONE); + + cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_AUDIO_MUTE, 0, 1, 1, 0); + if (has_ac3) + cxhdl->audio_ac3_bitrate = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_AC3_BITRATE, + V4L2_MPEG_AUDIO_AC3_BITRATE_448K, 0x03, + V4L2_MPEG_AUDIO_AC3_BITRATE_224K); + cxhdl->video_encoding = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_VIDEO_ENCODING, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 0, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2); + cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_VIDEO_ASPECT, + V4L2_MPEG_VIDEO_ASPECT_221x100, 0, + V4L2_MPEG_VIDEO_ASPECT_4x3); + cxhdl->video_b_frames = cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_B_FRAMES, 0, 33, 1, 2); + cxhdl->video_gop_size = cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, + 1, 34, 1, cxhdl->is_50hz ? 12 : 15); + cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, 0, 1, 1, 1); + cxhdl->video_bitrate_mode = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0, + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); + cxhdl->video_bitrate = cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_BITRATE, + 0, 27000000, 1, 6000000); + cxhdl->video_bitrate_peak = cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, + 0, 27000000, 1, 8000000); + cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION, 0, 255, 1, 0); + cxhdl->video_mute = cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_MUTE, 0, 1, 1, 0); + cxhdl->video_mute_yuv = cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_MUTE_YUV, 0, 0xffffff, 1, 0x008080); + + /* CX23415/6 specific */ + cxhdl->video_spatial_filter_mode = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE, + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 0, + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL); + cxhdl->video_spatial_filter = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER, + 0, 15, 1, 0); + cxhdl->video_luma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE, + 0, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR); + cxhdl->video_chroma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE, + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF, + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, + 0, + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR); + cxhdl->video_temporal_filter_mode = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE, + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL, + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO, + 0, + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL); + cxhdl->video_temporal_filter = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER, + 0, 31, 1, 8); + cxhdl->video_median_filter_type = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG, + 0, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF); + cxhdl->video_luma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM, + 0, 255, 1, 0); + cxhdl->video_luma_median_filter_top = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP, + 0, 255, 1, 255); + cxhdl->video_chroma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM, + 0, 255, 1, 0); + cxhdl->video_chroma_median_filter_top = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP, + 0, 255, 1, 255); + cx2341x_ctrl_new_custom(hdl, V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS, + 0, 1, 1, 0); + + if (hdl->error) { + int err = hdl->error; + + v4l2_ctrl_handler_free(hdl); + return err; + } + + v4l2_ctrl_cluster(8, &cxhdl->audio_sampling_freq); + v4l2_ctrl_cluster(2, &cxhdl->video_b_frames); + v4l2_ctrl_cluster(5, &cxhdl->stream_type); + v4l2_ctrl_cluster(2, &cxhdl->video_mute); + v4l2_ctrl_cluster(3, &cxhdl->video_spatial_filter_mode); + v4l2_ctrl_cluster(2, &cxhdl->video_luma_spatial_filter_type); + v4l2_ctrl_cluster(2, &cxhdl->video_spatial_filter); + v4l2_ctrl_cluster(4, &cxhdl->video_luma_median_filter_top); + + return 0; +} +EXPORT_SYMBOL(cx2341x_handler_init); + +void cx2341x_handler_set_50hz(struct cx2341x_handler *cxhdl, int is_50hz) +{ + cxhdl->is_50hz = is_50hz; + cxhdl->video_gop_size->default_value = cxhdl->is_50hz ? 12 : 15; +} +EXPORT_SYMBOL(cx2341x_handler_set_50hz); + +int cx2341x_handler_setup(struct cx2341x_handler *cxhdl) +{ + int h = cxhdl->height; + int w = cxhdl->width; + int err; + + err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_OUTPUT_PORT, 2, cxhdl->port, 0); + if (err) + return err; + err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_RATE, 1, cxhdl->is_50hz); + if (err) + return err; + + if (v4l2_ctrl_g_ctrl(cxhdl->video_encoding) == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) { + w /= 2; + h /= 2; + } + err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_SIZE, 2, h, w); + if (err) + return err; + return v4l2_ctrl_handler_setup(&cxhdl->hdl); +} +EXPORT_SYMBOL(cx2341x_handler_setup); + +void cx2341x_handler_set_busy(struct cx2341x_handler *cxhdl, int busy) +{ + v4l2_ctrl_grab(cxhdl->audio_sampling_freq, busy); + v4l2_ctrl_grab(cxhdl->audio_encoding, busy); + v4l2_ctrl_grab(cxhdl->audio_l2_bitrate, busy); + v4l2_ctrl_grab(cxhdl->audio_ac3_bitrate, busy); + v4l2_ctrl_grab(cxhdl->stream_vbi_fmt, busy); + v4l2_ctrl_grab(cxhdl->stream_type, busy); + v4l2_ctrl_grab(cxhdl->video_bitrate_mode, busy); + v4l2_ctrl_grab(cxhdl->video_bitrate, busy); + v4l2_ctrl_grab(cxhdl->video_bitrate_peak, busy); +} +EXPORT_SYMBOL(cx2341x_handler_set_busy); diff --git a/drivers/media/i2c/cx25840/Kconfig b/drivers/media/i2c/cx25840/Kconfig new file mode 100644 index 00000000000..451133ad41f --- /dev/null +++ b/drivers/media/i2c/cx25840/Kconfig @@ -0,0 +1,8 @@ +config VIDEO_CX25840 + tristate "Conexant CX2584x audio/video decoders" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Conexant CX2584x audio/video decoders. + + To compile this driver as a module, choose M here: the + module will be called cx25840 diff --git a/drivers/media/i2c/cx25840/Makefile b/drivers/media/i2c/cx25840/Makefile new file mode 100644 index 00000000000..898eb13340a --- /dev/null +++ b/drivers/media/i2c/cx25840/Makefile @@ -0,0 +1,6 @@ +cx25840-objs := cx25840-core.o cx25840-audio.o cx25840-firmware.o \ + cx25840-vbi.o cx25840-ir.o + +obj-$(CONFIG_VIDEO_CX25840) += cx25840.o + +ccflags-y += -Idrivers/media/i2c diff --git a/drivers/media/i2c/cx25840/cx25840-audio.c b/drivers/media/i2c/cx25840/cx25840-audio.c new file mode 100644 index 00000000000..34b96c7cfd6 --- /dev/null +++ b/drivers/media/i2c/cx25840/cx25840-audio.c @@ -0,0 +1,571 @@ +/* cx25840 audio functions + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include +#include +#include +#include + +#include "cx25840-core.h" + +/* + * Note: The PLL and SRC parameters are based on a reference frequency that + * would ideally be: + * + * NTSC Color subcarrier freq * 8 = 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz + * + * However, it's not the exact reference frequency that matters, only that the + * firmware and modules that comprise the driver for a particular board all + * use the same value (close to the ideal value). + * + * Comments below will note which reference frequency is assumed for various + * parameters. They will usually be one of + * + * ref_freq = 28.636360 MHz + * or + * ref_freq = 28.636363 MHz + */ + +static int cx25840_set_audclk_freq(struct i2c_client *client, u32 freq) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + + if (state->aud_input != CX25840_AUDIO_SERIAL) { + switch (freq) { + case 32000: + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x06, AUX PLL Post Divider = 0x10 + */ + cx25840_write4(client, 0x108, 0x1006040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x1bb39ee + * 28636363 * 0x6.dd9cf70/0x10 = 32000 * 384 + * 196.6 MHz pre-postdivide + * FIXME < 200 MHz is out of specified valid range + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x01bb39ee); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x50); + + if (is_cx2583x(state)) + break; + + /* src3/4/6_ctl */ + /* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */ + cx25840_write4(client, 0x900, 0x0801f77f); + cx25840_write4(client, 0x904, 0x0801f77f); + cx25840_write4(client, 0x90c, 0x0801f77f); + break; + + case 44100: + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x10 + */ + cx25840_write4(client, 0x108, 0x1009040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x0ec6bd6 + * 28636363 * 0x9.7635eb0/0x10 = 44100 * 384 + * 271 MHz pre-postdivide + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x00ec6bd6); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x50); + + if (is_cx2583x(state)) + break; + + /* src3/4/6_ctl */ + /* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */ + cx25840_write4(client, 0x900, 0x08016d59); + cx25840_write4(client, 0x904, 0x08016d59); + cx25840_write4(client, 0x90c, 0x08016d59); + break; + + case 48000: + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x10 + */ + cx25840_write4(client, 0x108, 0x100a040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x098d6e5 + * 28636363 * 0xa.4c6b728/0x10 = 48000 * 384 + * 295 MHz pre-postdivide + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x0098d6e5); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x50); + + if (is_cx2583x(state)) + break; + + /* src3/4/6_ctl */ + /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ + cx25840_write4(client, 0x900, 0x08014faa); + cx25840_write4(client, 0x904, 0x08014faa); + cx25840_write4(client, 0x90c, 0x08014faa); + break; + } + } else { + switch (freq) { + case 32000: + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x08, AUX PLL Post Divider = 0x1e + */ + cx25840_write4(client, 0x108, 0x1e08040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x12a0869 + * 28636363 * 0x8.9504348/0x1e = 32000 * 256 + * 246 MHz pre-postdivide + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x012a0869); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x14 = 256/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x54); + + if (is_cx2583x(state)) + break; + + /* src1_ctl */ + /* 0x1.0000 = 32000/32000 */ + cx25840_write4(client, 0x8f8, 0x08010000); + + /* src3/4/6_ctl */ + /* 0x2.0000 = 2 * (32000/32000) */ + cx25840_write4(client, 0x900, 0x08020000); + cx25840_write4(client, 0x904, 0x08020000); + cx25840_write4(client, 0x90c, 0x08020000); + break; + + case 44100: + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x18 + */ + cx25840_write4(client, 0x108, 0x1809040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x0ec6bd6 + * 28636363 * 0x9.7635eb0/0x18 = 44100 * 256 + * 271 MHz pre-postdivide + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x00ec6bd6); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x50); + + if (is_cx2583x(state)) + break; + + /* src1_ctl */ + /* 0x1.60cd = 44100/32000 */ + cx25840_write4(client, 0x8f8, 0x080160cd); + + /* src3/4/6_ctl */ + /* 0x1.7385 = 2 * (32000/44100) */ + cx25840_write4(client, 0x900, 0x08017385); + cx25840_write4(client, 0x904, 0x08017385); + cx25840_write4(client, 0x90c, 0x08017385); + break; + + case 48000: + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x18 + */ + cx25840_write4(client, 0x108, 0x180a040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x098d6e5 + * 28636363 * 0xa.4c6b728/0x18 = 48000 * 256 + * 295 MHz pre-postdivide + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x0098d6e5); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x50); + + if (is_cx2583x(state)) + break; + + /* src1_ctl */ + /* 0x1.8000 = 48000/32000 */ + cx25840_write4(client, 0x8f8, 0x08018000); + + /* src3/4/6_ctl */ + /* 0x1.5555 = 2 * (32000/48000) */ + cx25840_write4(client, 0x900, 0x08015555); + cx25840_write4(client, 0x904, 0x08015555); + cx25840_write4(client, 0x90c, 0x08015555); + break; + } + } + + state->audclk_freq = freq; + + return 0; +} + +static inline int cx25836_set_audclk_freq(struct i2c_client *client, u32 freq) +{ + return cx25840_set_audclk_freq(client, freq); +} + +static int cx23885_set_audclk_freq(struct i2c_client *client, u32 freq) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + + if (state->aud_input != CX25840_AUDIO_SERIAL) { + switch (freq) { + case 32000: + case 44100: + case 48000: + /* We don't have register values + * so avoid destroying registers. */ + /* FIXME return -EINVAL; */ + break; + } + } else { + switch (freq) { + case 32000: + case 44100: + /* We don't have register values + * so avoid destroying registers. */ + /* FIXME return -EINVAL; */ + break; + + case 48000: + /* src1_ctl */ + /* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */ + cx25840_write4(client, 0x8f8, 0x0801867c); + + /* src3/4/6_ctl */ + /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ + cx25840_write4(client, 0x900, 0x08014faa); + cx25840_write4(client, 0x904, 0x08014faa); + cx25840_write4(client, 0x90c, 0x08014faa); + break; + } + } + + state->audclk_freq = freq; + + return 0; +} + +static int cx231xx_set_audclk_freq(struct i2c_client *client, u32 freq) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + + if (state->aud_input != CX25840_AUDIO_SERIAL) { + switch (freq) { + case 32000: + /* src3/4/6_ctl */ + /* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */ + cx25840_write4(client, 0x900, 0x0801f77f); + cx25840_write4(client, 0x904, 0x0801f77f); + cx25840_write4(client, 0x90c, 0x0801f77f); + break; + + case 44100: + /* src3/4/6_ctl */ + /* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */ + cx25840_write4(client, 0x900, 0x08016d59); + cx25840_write4(client, 0x904, 0x08016d59); + cx25840_write4(client, 0x90c, 0x08016d59); + break; + + case 48000: + /* src3/4/6_ctl */ + /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ + cx25840_write4(client, 0x900, 0x08014faa); + cx25840_write4(client, 0x904, 0x08014faa); + cx25840_write4(client, 0x90c, 0x08014faa); + break; + } + } else { + switch (freq) { + /* FIXME These cases make different assumptions about audclk */ + case 32000: + /* src1_ctl */ + /* 0x1.0000 = 32000/32000 */ + cx25840_write4(client, 0x8f8, 0x08010000); + + /* src3/4/6_ctl */ + /* 0x2.0000 = 2 * (32000/32000) */ + cx25840_write4(client, 0x900, 0x08020000); + cx25840_write4(client, 0x904, 0x08020000); + cx25840_write4(client, 0x90c, 0x08020000); + break; + + case 44100: + /* src1_ctl */ + /* 0x1.60cd = 44100/32000 */ + cx25840_write4(client, 0x8f8, 0x080160cd); + + /* src3/4/6_ctl */ + /* 0x1.7385 = 2 * (32000/44100) */ + cx25840_write4(client, 0x900, 0x08017385); + cx25840_write4(client, 0x904, 0x08017385); + cx25840_write4(client, 0x90c, 0x08017385); + break; + + case 48000: + /* src1_ctl */ + /* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */ + cx25840_write4(client, 0x8f8, 0x0801867c); + + /* src3/4/6_ctl */ + /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ + cx25840_write4(client, 0x900, 0x08014faa); + cx25840_write4(client, 0x904, 0x08014faa); + cx25840_write4(client, 0x90c, 0x08014faa); + break; + } + } + + state->audclk_freq = freq; + + return 0; +} + +static int set_audclk_freq(struct i2c_client *client, u32 freq) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + + if (freq != 32000 && freq != 44100 && freq != 48000) + return -EINVAL; + + if (is_cx231xx(state)) + return cx231xx_set_audclk_freq(client, freq); + + if (is_cx2388x(state)) + return cx23885_set_audclk_freq(client, freq); + + if (is_cx2583x(state)) + return cx25836_set_audclk_freq(client, freq); + + return cx25840_set_audclk_freq(client, freq); +} + +void cx25840_audio_set_path(struct i2c_client *client) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + + if (!is_cx2583x(state)) { + /* assert soft reset */ + cx25840_and_or(client, 0x810, ~0x1, 0x01); + + /* stop microcontroller */ + cx25840_and_or(client, 0x803, ~0x10, 0); + + /* Mute everything to prevent the PFFT! */ + cx25840_write(client, 0x8d3, 0x1f); + + if (state->aud_input == CX25840_AUDIO_SERIAL) { + /* Set Path1 to Serial Audio Input */ + cx25840_write4(client, 0x8d0, 0x01011012); + + /* The microcontroller should not be started for the + * non-tuner inputs: autodetection is specific for + * TV audio. */ + } else { + /* Set Path1 to Analog Demod Main Channel */ + cx25840_write4(client, 0x8d0, 0x1f063870); + } + } + + set_audclk_freq(client, state->audclk_freq); + + if (!is_cx2583x(state)) { + if (state->aud_input != CX25840_AUDIO_SERIAL) { + /* When the microcontroller detects the + * audio format, it will unmute the lines */ + cx25840_and_or(client, 0x803, ~0x10, 0x10); + } + + /* deassert soft reset */ + cx25840_and_or(client, 0x810, ~0x1, 0x00); + + /* Ensure the controller is running when we exit */ + if (is_cx2388x(state) || is_cx231xx(state)) + cx25840_and_or(client, 0x803, ~0x10, 0x10); + } +} + +static void set_volume(struct i2c_client *client, int volume) +{ + int vol; + + /* Convert the volume to msp3400 values (0-127) */ + vol = volume >> 9; + + /* now scale it up to cx25840 values + * -114dB to -96dB maps to 0 + * this should be 19, but in my testing that was 4dB too loud */ + if (vol <= 23) { + vol = 0; + } else { + vol -= 23; + } + + /* PATH1_VOLUME */ + cx25840_write(client, 0x8d4, 228 - (vol * 2)); +} + +static void set_balance(struct i2c_client *client, int balance) +{ + int bal = balance >> 8; + if (bal > 0x80) { + /* PATH1_BAL_LEFT */ + cx25840_and_or(client, 0x8d5, 0x7f, 0x80); + /* PATH1_BAL_LEVEL */ + cx25840_and_or(client, 0x8d5, ~0x7f, bal & 0x7f); + } else { + /* PATH1_BAL_LEFT */ + cx25840_and_or(client, 0x8d5, 0x7f, 0x00); + /* PATH1_BAL_LEVEL */ + cx25840_and_or(client, 0x8d5, ~0x7f, 0x80 - bal); + } +} + +int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct cx25840_state *state = to_state(sd); + int retval; + + if (!is_cx2583x(state)) + cx25840_and_or(client, 0x810, ~0x1, 1); + if (state->aud_input != CX25840_AUDIO_SERIAL) { + cx25840_and_or(client, 0x803, ~0x10, 0); + cx25840_write(client, 0x8d3, 0x1f); + } + retval = set_audclk_freq(client, freq); + if (state->aud_input != CX25840_AUDIO_SERIAL) + cx25840_and_or(client, 0x803, ~0x10, 0x10); + if (!is_cx2583x(state)) + cx25840_and_or(client, 0x810, ~0x1, 0); + return retval; +} + +static int cx25840_audio_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + if (state->mute->val) + set_volume(client, 0); + else + set_volume(client, state->volume->val); + break; + case V4L2_CID_AUDIO_BASS: + /* PATH1_EQ_BASS_VOL */ + cx25840_and_or(client, 0x8d9, ~0x3f, + 48 - (ctrl->val * 48 / 0xffff)); + break; + case V4L2_CID_AUDIO_TREBLE: + /* PATH1_EQ_TREBLE_VOL */ + cx25840_and_or(client, 0x8db, ~0x3f, + 48 - (ctrl->val * 48 / 0xffff)); + break; + case V4L2_CID_AUDIO_BALANCE: + set_balance(client, ctrl->val); + break; + default: + return -EINVAL; + } + return 0; +} + +const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops = { + .s_ctrl = cx25840_audio_s_ctrl, +}; diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c new file mode 100644 index 00000000000..d8eac3e30a7 --- /dev/null +++ b/drivers/media/i2c/cx25840/cx25840-core.c @@ -0,0 +1,5340 @@ +/* cx25840 - Conexant CX25840 audio/video decoder driver + * + * Copyright (C) 2004 Ulf Eklund + * + * Based on the saa7115 driver and on the first version of Chris Kennedy's + * cx25840 driver. + * + * Changes by Tyler Trafford + * - cleanup/rewrite for V4L2 API (2005) + * + * VBI support by Hans Verkuil . + * + * NTSC sliced VBI support by Christopher Neufeld + * with additional fixes by Hans Verkuil . + * + * CX23885 support by Steven Toth . + * + * CX2388[578] IRQ handling, IO Pin mux configuration and other small fixes are + * Copyright (C) 2010 Andy Walls + * + * CX23888 DIF support for the HVR1850 + * Copyright (C) 2011 Steven Toth + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cx25840-core.h" + +MODULE_DESCRIPTION("Conexant CX25840 audio/video decoder driver"); +MODULE_AUTHOR("Ulf Eklund, Chris Kennedy, Hans Verkuil, Tyler Trafford"); +MODULE_LICENSE("GPL"); + +#define CX25840_VID_INT_STAT_REG 0x410 +#define CX25840_VID_INT_STAT_BITS 0x0000ffff +#define CX25840_VID_INT_MASK_BITS 0xffff0000 +#define CX25840_VID_INT_MASK_SHFT 16 +#define CX25840_VID_INT_MASK_REG 0x412 + +#define CX23885_AUD_MC_INT_MASK_REG 0x80c +#define CX23885_AUD_MC_INT_STAT_BITS 0xffff0000 +#define CX23885_AUD_MC_INT_CTRL_BITS 0x0000ffff +#define CX23885_AUD_MC_INT_STAT_SHFT 16 + +#define CX25840_AUD_INT_CTRL_REG 0x812 +#define CX25840_AUD_INT_STAT_REG 0x813 + +#define CX23885_PIN_CTRL_IRQ_REG 0x123 +#define CX23885_PIN_CTRL_IRQ_IR_STAT 0x40 +#define CX23885_PIN_CTRL_IRQ_AUD_STAT 0x20 +#define CX23885_PIN_CTRL_IRQ_VID_STAT 0x10 + +#define CX25840_IR_STATS_REG 0x210 +#define CX25840_IR_IRQEN_REG 0x214 + +static int cx25840_debug; + +module_param_named(debug,cx25840_debug, int, 0644); + +MODULE_PARM_DESC(debug, "Debugging messages [0=Off (default) 1=On]"); + + +/* ----------------------------------------------------------------------- */ +static void cx23888_std_setup(struct i2c_client *client); + +int cx25840_write(struct i2c_client *client, u16 addr, u8 value) +{ + u8 buffer[3]; + buffer[0] = addr >> 8; + buffer[1] = addr & 0xff; + buffer[2] = value; + return i2c_master_send(client, buffer, 3); +} + +int cx25840_write4(struct i2c_client *client, u16 addr, u32 value) +{ + u8 buffer[6]; + buffer[0] = addr >> 8; + buffer[1] = addr & 0xff; + buffer[2] = value & 0xff; + buffer[3] = (value >> 8) & 0xff; + buffer[4] = (value >> 16) & 0xff; + buffer[5] = value >> 24; + return i2c_master_send(client, buffer, 6); +} + +u8 cx25840_read(struct i2c_client * client, u16 addr) +{ + struct i2c_msg msgs[2]; + u8 tx_buf[2], rx_buf[1]; + + /* Write register address */ + tx_buf[0] = addr >> 8; + tx_buf[1] = addr & 0xff; + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (char *) tx_buf; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = 1; + msgs[1].buf = (char *) rx_buf; + + if (i2c_transfer(client->adapter, msgs, 2) < 2) + return 0; + + return rx_buf[0]; +} + +u32 cx25840_read4(struct i2c_client * client, u16 addr) +{ + struct i2c_msg msgs[2]; + u8 tx_buf[2], rx_buf[4]; + + /* Write register address */ + tx_buf[0] = addr >> 8; + tx_buf[1] = addr & 0xff; + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (char *) tx_buf; + + /* Read data from registers */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = 4; + msgs[1].buf = (char *) rx_buf; + + if (i2c_transfer(client->adapter, msgs, 2) < 2) + return 0; + + return (rx_buf[3] << 24) | (rx_buf[2] << 16) | (rx_buf[1] << 8) | + rx_buf[0]; +} + +int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned and_mask, + u8 or_value) +{ + return cx25840_write(client, addr, + (cx25840_read(client, addr) & and_mask) | + or_value); +} + +int cx25840_and_or4(struct i2c_client *client, u16 addr, u32 and_mask, + u32 or_value) +{ + return cx25840_write4(client, addr, + (cx25840_read4(client, addr) & and_mask) | + or_value); +} + +/* ----------------------------------------------------------------------- */ + +static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input, + enum cx25840_audio_input aud_input); + +/* ----------------------------------------------------------------------- */ + +static int cx23885_s_io_pin_config(struct v4l2_subdev *sd, size_t n, + struct v4l2_subdev_io_pin_config *p) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int i; + u32 pin_ctrl; + u8 gpio_oe, gpio_data, strength; + + pin_ctrl = cx25840_read4(client, 0x120); + gpio_oe = cx25840_read(client, 0x160); + gpio_data = cx25840_read(client, 0x164); + + for (i = 0; i < n; i++) { + strength = p[i].strength; + if (strength > CX25840_PIN_DRIVE_FAST) + strength = CX25840_PIN_DRIVE_FAST; + + switch (p[i].pin) { + case CX23885_PIN_IRQ_N_GPIO16: + if (p[i].function != CX23885_PAD_IRQ_N) { + /* GPIO16 */ + pin_ctrl &= ~(0x1 << 25); + } else { + /* IRQ_N */ + if (p[i].flags & + (V4L2_SUBDEV_IO_PIN_DISABLE | + V4L2_SUBDEV_IO_PIN_INPUT)) { + pin_ctrl &= ~(0x1 << 25); + } else { + pin_ctrl |= (0x1 << 25); + } + if (p[i].flags & + V4L2_SUBDEV_IO_PIN_ACTIVE_LOW) { + pin_ctrl &= ~(0x1 << 24); + } else { + pin_ctrl |= (0x1 << 24); + } + } + break; + case CX23885_PIN_IR_RX_GPIO19: + if (p[i].function != CX23885_PAD_GPIO19) { + /* IR_RX */ + gpio_oe |= (0x1 << 0); + pin_ctrl &= ~(0x3 << 18); + pin_ctrl |= (strength << 18); + } else { + /* GPIO19 */ + gpio_oe &= ~(0x1 << 0); + if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { + gpio_data &= ~(0x1 << 0); + gpio_data |= ((p[i].value & 0x1) << 0); + } + pin_ctrl &= ~(0x3 << 12); + pin_ctrl |= (strength << 12); + } + break; + case CX23885_PIN_IR_TX_GPIO20: + if (p[i].function != CX23885_PAD_GPIO20) { + /* IR_TX */ + gpio_oe |= (0x1 << 1); + if (p[i].flags & V4L2_SUBDEV_IO_PIN_DISABLE) + pin_ctrl &= ~(0x1 << 10); + else + pin_ctrl |= (0x1 << 10); + pin_ctrl &= ~(0x3 << 18); + pin_ctrl |= (strength << 18); + } else { + /* GPIO20 */ + gpio_oe &= ~(0x1 << 1); + if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { + gpio_data &= ~(0x1 << 1); + gpio_data |= ((p[i].value & 0x1) << 1); + } + pin_ctrl &= ~(0x3 << 12); + pin_ctrl |= (strength << 12); + } + break; + case CX23885_PIN_I2S_SDAT_GPIO21: + if (p[i].function != CX23885_PAD_GPIO21) { + /* I2S_SDAT */ + /* TODO: Input or Output config */ + gpio_oe |= (0x1 << 2); + pin_ctrl &= ~(0x3 << 22); + pin_ctrl |= (strength << 22); + } else { + /* GPIO21 */ + gpio_oe &= ~(0x1 << 2); + if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { + gpio_data &= ~(0x1 << 2); + gpio_data |= ((p[i].value & 0x1) << 2); + } + pin_ctrl &= ~(0x3 << 12); + pin_ctrl |= (strength << 12); + } + break; + case CX23885_PIN_I2S_WCLK_GPIO22: + if (p[i].function != CX23885_PAD_GPIO22) { + /* I2S_WCLK */ + /* TODO: Input or Output config */ + gpio_oe |= (0x1 << 3); + pin_ctrl &= ~(0x3 << 22); + pin_ctrl |= (strength << 22); + } else { + /* GPIO22 */ + gpio_oe &= ~(0x1 << 3); + if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { + gpio_data &= ~(0x1 << 3); + gpio_data |= ((p[i].value & 0x1) << 3); + } + pin_ctrl &= ~(0x3 << 12); + pin_ctrl |= (strength << 12); + } + break; + case CX23885_PIN_I2S_BCLK_GPIO23: + if (p[i].function != CX23885_PAD_GPIO23) { + /* I2S_BCLK */ + /* TODO: Input or Output config */ + gpio_oe |= (0x1 << 4); + pin_ctrl &= ~(0x3 << 22); + pin_ctrl |= (strength << 22); + } else { + /* GPIO23 */ + gpio_oe &= ~(0x1 << 4); + if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { + gpio_data &= ~(0x1 << 4); + gpio_data |= ((p[i].value & 0x1) << 4); + } + pin_ctrl &= ~(0x3 << 12); + pin_ctrl |= (strength << 12); + } + break; + } + } + + cx25840_write(client, 0x164, gpio_data); + cx25840_write(client, 0x160, gpio_oe); + cx25840_write4(client, 0x120, pin_ctrl); + return 0; +} + +static int common_s_io_pin_config(struct v4l2_subdev *sd, size_t n, + struct v4l2_subdev_io_pin_config *pincfg) +{ + struct cx25840_state *state = to_state(sd); + + if (is_cx2388x(state)) + return cx23885_s_io_pin_config(sd, n, pincfg); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static void init_dll1(struct i2c_client *client) +{ + /* This is the Hauppauge sequence used to + * initialize the Delay Lock Loop 1 (ADC DLL). */ + cx25840_write(client, 0x159, 0x23); + cx25840_write(client, 0x15a, 0x87); + cx25840_write(client, 0x15b, 0x06); + udelay(10); + cx25840_write(client, 0x159, 0xe1); + udelay(10); + cx25840_write(client, 0x15a, 0x86); + cx25840_write(client, 0x159, 0xe0); + cx25840_write(client, 0x159, 0xe1); + cx25840_write(client, 0x15b, 0x10); +} + +static void init_dll2(struct i2c_client *client) +{ + /* This is the Hauppauge sequence used to + * initialize the Delay Lock Loop 2 (ADC DLL). */ + cx25840_write(client, 0x15d, 0xe3); + cx25840_write(client, 0x15e, 0x86); + cx25840_write(client, 0x15f, 0x06); + udelay(10); + cx25840_write(client, 0x15d, 0xe1); + cx25840_write(client, 0x15d, 0xe0); + cx25840_write(client, 0x15d, 0xe1); +} + +static void cx25836_initialize(struct i2c_client *client) +{ + /* reset configuration is described on page 3-77 of the CX25836 datasheet */ + /* 2. */ + cx25840_and_or(client, 0x000, ~0x01, 0x01); + cx25840_and_or(client, 0x000, ~0x01, 0x00); + /* 3a. */ + cx25840_and_or(client, 0x15a, ~0x70, 0x00); + /* 3b. */ + cx25840_and_or(client, 0x15b, ~0x1e, 0x06); + /* 3c. */ + cx25840_and_or(client, 0x159, ~0x02, 0x02); + /* 3d. */ + udelay(10); + /* 3e. */ + cx25840_and_or(client, 0x159, ~0x02, 0x00); + /* 3f. */ + cx25840_and_or(client, 0x159, ~0xc0, 0xc0); + /* 3g. */ + cx25840_and_or(client, 0x159, ~0x01, 0x00); + cx25840_and_or(client, 0x159, ~0x01, 0x01); + /* 3h. */ + cx25840_and_or(client, 0x15b, ~0x1e, 0x10); +} + +static void cx25840_work_handler(struct work_struct *work) +{ + struct cx25840_state *state = container_of(work, struct cx25840_state, fw_work); + cx25840_loadfw(state->c); + wake_up(&state->fw_wait); +} + +static void cx25840_initialize(struct i2c_client *client) +{ + DEFINE_WAIT(wait); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + struct workqueue_struct *q; + + /* datasheet startup in numbered steps, refer to page 3-77 */ + /* 2. */ + cx25840_and_or(client, 0x803, ~0x10, 0x00); + /* The default of this register should be 4, but I get 0 instead. + * Set this register to 4 manually. */ + cx25840_write(client, 0x000, 0x04); + /* 3. */ + init_dll1(client); + init_dll2(client); + cx25840_write(client, 0x136, 0x0a); + /* 4. */ + cx25840_write(client, 0x13c, 0x01); + cx25840_write(client, 0x13c, 0x00); + /* 5. */ + /* Do the firmware load in a work handler to prevent. + Otherwise the kernel is blocked waiting for the + bit-banging i2c interface to finish uploading the + firmware. */ + INIT_WORK(&state->fw_work, cx25840_work_handler); + init_waitqueue_head(&state->fw_wait); + q = create_singlethread_workqueue("cx25840_fw"); + prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE); + queue_work(q, &state->fw_work); + schedule(); + finish_wait(&state->fw_wait, &wait); + destroy_workqueue(q); + + /* 6. */ + cx25840_write(client, 0x115, 0x8c); + cx25840_write(client, 0x116, 0x07); + cx25840_write(client, 0x118, 0x02); + /* 7. */ + cx25840_write(client, 0x4a5, 0x80); + cx25840_write(client, 0x4a5, 0x00); + cx25840_write(client, 0x402, 0x00); + /* 8. */ + cx25840_and_or(client, 0x401, ~0x18, 0); + cx25840_and_or(client, 0x4a2, ~0x10, 0x10); + /* steps 8c and 8d are done in change_input() */ + /* 10. */ + cx25840_write(client, 0x8d3, 0x1f); + cx25840_write(client, 0x8e3, 0x03); + + cx25840_std_setup(client); + + /* trial and error says these are needed to get audio */ + cx25840_write(client, 0x914, 0xa0); + cx25840_write(client, 0x918, 0xa0); + cx25840_write(client, 0x919, 0x01); + + /* stereo preferred */ + cx25840_write(client, 0x809, 0x04); + /* AC97 shift */ + cx25840_write(client, 0x8cf, 0x0f); + + /* (re)set input */ + set_input(client, state->vid_input, state->aud_input); + + /* start microcontroller */ + cx25840_and_or(client, 0x803, ~0x10, 0x10); +} + +static void cx23885_initialize(struct i2c_client *client) +{ + DEFINE_WAIT(wait); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + struct workqueue_struct *q; + + /* + * Come out of digital power down + * The CX23888, at least, needs this, otherwise registers aside from + * 0x0-0x2 can't be read or written. + */ + cx25840_write(client, 0x000, 0); + + /* Internal Reset */ + cx25840_and_or(client, 0x102, ~0x01, 0x01); + cx25840_and_or(client, 0x102, ~0x01, 0x00); + + /* Stop microcontroller */ + cx25840_and_or(client, 0x803, ~0x10, 0x00); + + /* DIF in reset? */ + cx25840_write(client, 0x398, 0); + + /* + * Trust the default xtal, no division + * '885: 28.636363... MHz + * '887: 25.000000 MHz + * '888: 50.000000 MHz + */ + cx25840_write(client, 0x2, 0x76); + + /* Power up all the PLL's and DLL */ + cx25840_write(client, 0x1, 0x40); + + /* Sys PLL */ + switch (state->id) { + case V4L2_IDENT_CX23888_AV: + /* + * 50.0 MHz * (0xb + 0xe8ba26/0x2000000)/4 = 5 * 28.636363 MHz + * 572.73 MHz before post divide + */ + /* HVR1850 or 50MHz xtal */ + cx25840_write(client, 0x2, 0x71); + cx25840_write4(client, 0x11c, 0x01d1744c); + cx25840_write4(client, 0x118, 0x00000416); + cx25840_write4(client, 0x404, 0x0010253e); + cx25840_write4(client, 0x42c, 0x42600000); + cx25840_write4(client, 0x44c, 0x161f1000); + break; + case V4L2_IDENT_CX23887_AV: + /* + * 25.0 MHz * (0x16 + 0x1d1744c/0x2000000)/4 = 5 * 28.636363 MHz + * 572.73 MHz before post divide + */ + cx25840_write4(client, 0x11c, 0x01d1744c); + cx25840_write4(client, 0x118, 0x00000416); + break; + case V4L2_IDENT_CX23885_AV: + default: + /* + * 28.636363 MHz * (0x14 + 0x0/0x2000000)/4 = 5 * 28.636363 MHz + * 572.73 MHz before post divide + */ + cx25840_write4(client, 0x11c, 0x00000000); + cx25840_write4(client, 0x118, 0x00000414); + break; + } + + /* Disable DIF bypass */ + cx25840_write4(client, 0x33c, 0x00000001); + + /* DIF Src phase inc */ + cx25840_write4(client, 0x340, 0x0df7df83); + + /* + * Vid PLL + * Setup for a BT.656 pixel clock of 13.5 Mpixels/second + * + * 28.636363 MHz * (0xf + 0x02be2c9/0x2000000)/4 = 8 * 13.5 MHz + * 432.0 MHz before post divide + */ + + /* HVR1850 */ + switch (state->id) { + case V4L2_IDENT_CX23888_AV: + /* 888/HVR1250 specific */ + cx25840_write4(client, 0x10c, 0x13333333); + cx25840_write4(client, 0x108, 0x00000515); + break; + default: + cx25840_write4(client, 0x10c, 0x002be2c9); + cx25840_write4(client, 0x108, 0x0000040f); + } + + /* Luma */ + cx25840_write4(client, 0x414, 0x00107d12); + + /* Chroma */ + cx25840_write4(client, 0x420, 0x3d008282); + + /* + * Aux PLL + * Initial setup for audio sample clock: + * 48 ksps, 16 bits/sample, x160 multiplier = 122.88 MHz + * Initial I2S output/master clock(?): + * 48 ksps, 16 bits/sample, x16 multiplier = 12.288 MHz + */ + switch (state->id) { + case V4L2_IDENT_CX23888_AV: + /* + * 50.0 MHz * (0x7 + 0x0bedfa4/0x2000000)/3 = 122.88 MHz + * 368.64 MHz before post divide + * 122.88 MHz / 0xa = 12.288 MHz + */ + /* HVR1850 or 50MHz xtal */ + cx25840_write4(client, 0x114, 0x017dbf48); + cx25840_write4(client, 0x110, 0x000a030e); + break; + case V4L2_IDENT_CX23887_AV: + /* + * 25.0 MHz * (0xe + 0x17dbf48/0x2000000)/3 = 122.88 MHz + * 368.64 MHz before post divide + * 122.88 MHz / 0xa = 12.288 MHz + */ + cx25840_write4(client, 0x114, 0x017dbf48); + cx25840_write4(client, 0x110, 0x000a030e); + break; + case V4L2_IDENT_CX23885_AV: + default: + /* + * 28.636363 MHz * (0xc + 0x1bf0c9e/0x2000000)/3 = 122.88 MHz + * 368.64 MHz before post divide + * 122.88 MHz / 0xa = 12.288 MHz + */ + cx25840_write4(client, 0x114, 0x01bf0c9e); + cx25840_write4(client, 0x110, 0x000a030c); + break; + }; + + /* ADC2 input select */ + cx25840_write(client, 0x102, 0x10); + + /* VIN1 & VIN5 */ + cx25840_write(client, 0x103, 0x11); + + /* Enable format auto detect */ + cx25840_write(client, 0x400, 0); + /* Fast subchroma lock */ + /* White crush, Chroma AGC & Chroma Killer enabled */ + cx25840_write(client, 0x401, 0xe8); + + /* Select AFE clock pad output source */ + cx25840_write(client, 0x144, 0x05); + + /* Drive GPIO2 direction and values for HVR1700 + * where an onboard mux selects the output of demodulator + * vs the 417. Failure to set this results in no DTV. + * It's safe to set this across all Hauppauge boards + * currently, regardless of the board type. + */ + cx25840_write(client, 0x160, 0x1d); + cx25840_write(client, 0x164, 0x00); + + /* Do the firmware load in a work handler to prevent. + Otherwise the kernel is blocked waiting for the + bit-banging i2c interface to finish uploading the + firmware. */ + INIT_WORK(&state->fw_work, cx25840_work_handler); + init_waitqueue_head(&state->fw_wait); + q = create_singlethread_workqueue("cx25840_fw"); + prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE); + queue_work(q, &state->fw_work); + schedule(); + finish_wait(&state->fw_wait, &wait); + destroy_workqueue(q); + + /* Call the cx23888 specific std setup func, we no longer rely on + * the generic cx24840 func. + */ + if (is_cx23888(state)) + cx23888_std_setup(client); + else + cx25840_std_setup(client); + + /* (re)set input */ + set_input(client, state->vid_input, state->aud_input); + + /* start microcontroller */ + cx25840_and_or(client, 0x803, ~0x10, 0x10); + + /* Disable and clear video interrupts - we don't use them */ + cx25840_write4(client, CX25840_VID_INT_STAT_REG, 0xffffffff); + + /* Disable and clear audio interrupts - we don't use them */ + cx25840_write(client, CX25840_AUD_INT_CTRL_REG, 0xff); + cx25840_write(client, CX25840_AUD_INT_STAT_REG, 0xff); + + /* CC raw enable */ + /* - VIP 1.1 control codes - 10bit, blue field enable. + * - enable raw data during vertical blanking. + * - enable ancillary Data insertion for 656 or VIP. + */ + cx25840_write4(client, 0x404, 0x0010253e); + + /* CC on - Undocumented Register */ + cx25840_write(client, 0x42f, 0x66); + + /* HVR-1250 / HVR1850 DIF related */ + /* Power everything up */ + cx25840_write4(client, 0x130, 0x0); + + /* Undocumented */ + cx25840_write4(client, 0x478, 0x6628021F); + + /* AFE_CLK_OUT_CTRL - Select the clock output source as output */ + cx25840_write4(client, 0x144, 0x5); + + /* I2C_OUT_CTL - I2S output configuration as + * Master, Sony, Left justified, left sample on WS=1 + */ + cx25840_write4(client, 0x918, 0x1a0); + + /* AFE_DIAG_CTRL1 */ + cx25840_write4(client, 0x134, 0x000a1800); + + /* AFE_DIAG_CTRL3 - Inverted Polarity for Audio and Video */ + cx25840_write4(client, 0x13c, 0x00310000); +} + +/* ----------------------------------------------------------------------- */ + +static void cx231xx_initialize(struct i2c_client *client) +{ + DEFINE_WAIT(wait); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + struct workqueue_struct *q; + + /* Internal Reset */ + cx25840_and_or(client, 0x102, ~0x01, 0x01); + cx25840_and_or(client, 0x102, ~0x01, 0x00); + + /* Stop microcontroller */ + cx25840_and_or(client, 0x803, ~0x10, 0x00); + + /* DIF in reset? */ + cx25840_write(client, 0x398, 0); + + /* Trust the default xtal, no division */ + /* This changes for the cx23888 products */ + cx25840_write(client, 0x2, 0x76); + + /* Bring down the regulator for AUX clk */ + cx25840_write(client, 0x1, 0x40); + + /* Disable DIF bypass */ + cx25840_write4(client, 0x33c, 0x00000001); + + /* DIF Src phase inc */ + cx25840_write4(client, 0x340, 0x0df7df83); + + /* Luma */ + cx25840_write4(client, 0x414, 0x00107d12); + + /* Chroma */ + cx25840_write4(client, 0x420, 0x3d008282); + + /* ADC2 input select */ + cx25840_write(client, 0x102, 0x10); + + /* VIN1 & VIN5 */ + cx25840_write(client, 0x103, 0x11); + + /* Enable format auto detect */ + cx25840_write(client, 0x400, 0); + /* Fast subchroma lock */ + /* White crush, Chroma AGC & Chroma Killer enabled */ + cx25840_write(client, 0x401, 0xe8); + + /* Do the firmware load in a work handler to prevent. + Otherwise the kernel is blocked waiting for the + bit-banging i2c interface to finish uploading the + firmware. */ + INIT_WORK(&state->fw_work, cx25840_work_handler); + init_waitqueue_head(&state->fw_wait); + q = create_singlethread_workqueue("cx25840_fw"); + prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE); + queue_work(q, &state->fw_work); + schedule(); + finish_wait(&state->fw_wait, &wait); + destroy_workqueue(q); + + cx25840_std_setup(client); + + /* (re)set input */ + set_input(client, state->vid_input, state->aud_input); + + /* start microcontroller */ + cx25840_and_or(client, 0x803, ~0x10, 0x10); + + /* CC raw enable */ + cx25840_write(client, 0x404, 0x0b); + + /* CC on */ + cx25840_write(client, 0x42f, 0x66); + cx25840_write4(client, 0x474, 0x1e1e601a); +} + +/* ----------------------------------------------------------------------- */ + +void cx25840_std_setup(struct i2c_client *client) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + v4l2_std_id std = state->std; + int hblank, hactive, burst, vblank, vactive, sc; + int vblank656, src_decimation; + int luma_lpf, uv_lpf, comb; + u32 pll_int, pll_frac, pll_post; + + /* datasheet startup, step 8d */ + if (std & ~V4L2_STD_NTSC) + cx25840_write(client, 0x49f, 0x11); + else + cx25840_write(client, 0x49f, 0x14); + + if (std & V4L2_STD_625_50) { + hblank = 132; + hactive = 720; + burst = 93; + vblank = 36; + vactive = 580; + vblank656 = 40; + src_decimation = 0x21f; + luma_lpf = 2; + + if (std & V4L2_STD_SECAM) { + uv_lpf = 0; + comb = 0; + sc = 0x0a425f; + } else if (std == V4L2_STD_PAL_Nc) { + uv_lpf = 1; + comb = 0x20; + sc = 556453; + } else { + uv_lpf = 1; + comb = 0x20; + sc = 688739; + } + } else { + hactive = 720; + hblank = 122; + vactive = 487; + luma_lpf = 1; + uv_lpf = 1; + + src_decimation = 0x21f; + if (std == V4L2_STD_PAL_60) { + vblank = 26; + vblank656 = 26; + burst = 0x5b; + luma_lpf = 2; + comb = 0x20; + sc = 688739; + } else if (std == V4L2_STD_PAL_M) { + vblank = 20; + vblank656 = 24; + burst = 0x61; + comb = 0x20; + sc = 555452; + } else { + vblank = 26; + vblank656 = 26; + burst = 0x5b; + comb = 0x66; + sc = 556063; + } + } + + /* DEBUG: Displays configured PLL frequency */ + if (!is_cx231xx(state)) { + pll_int = cx25840_read(client, 0x108); + pll_frac = cx25840_read4(client, 0x10c) & 0x1ffffff; + pll_post = cx25840_read(client, 0x109); + v4l_dbg(1, cx25840_debug, client, + "PLL regs = int: %u, frac: %u, post: %u\n", + pll_int, pll_frac, pll_post); + + if (pll_post) { + int fin, fsc; + int pll = (28636363L * ((((u64)pll_int) << 25L) + pll_frac)) >> 25L; + + pll /= pll_post; + v4l_dbg(1, cx25840_debug, client, "PLL = %d.%06d MHz\n", + pll / 1000000, pll % 1000000); + v4l_dbg(1, cx25840_debug, client, "PLL/8 = %d.%06d MHz\n", + pll / 8000000, (pll / 8) % 1000000); + + fin = ((u64)src_decimation * pll) >> 12; + v4l_dbg(1, cx25840_debug, client, + "ADC Sampling freq = %d.%06d MHz\n", + fin / 1000000, fin % 1000000); + + fsc = (((u64)sc) * pll) >> 24L; + v4l_dbg(1, cx25840_debug, client, + "Chroma sub-carrier freq = %d.%06d MHz\n", + fsc / 1000000, fsc % 1000000); + + v4l_dbg(1, cx25840_debug, client, "hblank %i, hactive %i, " + "vblank %i, vactive %i, vblank656 %i, src_dec %i, " + "burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x, " + "sc 0x%06x\n", + hblank, hactive, vblank, vactive, vblank656, + src_decimation, burst, luma_lpf, uv_lpf, comb, sc); + } + } + + /* Sets horizontal blanking delay and active lines */ + cx25840_write(client, 0x470, hblank); + cx25840_write(client, 0x471, + 0xff & (((hblank >> 8) & 0x3) | (hactive << 4))); + cx25840_write(client, 0x472, hactive >> 4); + + /* Sets burst gate delay */ + cx25840_write(client, 0x473, burst); + + /* Sets vertical blanking delay and active duration */ + cx25840_write(client, 0x474, vblank); + cx25840_write(client, 0x475, + 0xff & (((vblank >> 8) & 0x3) | (vactive << 4))); + cx25840_write(client, 0x476, vactive >> 4); + cx25840_write(client, 0x477, vblank656); + + /* Sets src decimation rate */ + cx25840_write(client, 0x478, 0xff & src_decimation); + cx25840_write(client, 0x479, 0xff & (src_decimation >> 8)); + + /* Sets Luma and UV Low pass filters */ + cx25840_write(client, 0x47a, luma_lpf << 6 | ((uv_lpf << 4) & 0x30)); + + /* Enables comb filters */ + cx25840_write(client, 0x47b, comb); + + /* Sets SC Step*/ + cx25840_write(client, 0x47c, sc); + cx25840_write(client, 0x47d, 0xff & sc >> 8); + cx25840_write(client, 0x47e, 0xff & sc >> 16); + + /* Sets VBI parameters */ + if (std & V4L2_STD_625_50) { + cx25840_write(client, 0x47f, 0x01); + state->vbi_line_offset = 5; + } else { + cx25840_write(client, 0x47f, 0x00); + state->vbi_line_offset = 8; + } +} + +/* ----------------------------------------------------------------------- */ + +static void input_change(struct i2c_client *client) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + v4l2_std_id std = state->std; + + /* Follow step 8c and 8d of section 3.16 in the cx25840 datasheet */ + if (std & V4L2_STD_SECAM) { + cx25840_write(client, 0x402, 0); + } + else { + cx25840_write(client, 0x402, 0x04); + cx25840_write(client, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11); + } + cx25840_and_or(client, 0x401, ~0x60, 0); + cx25840_and_or(client, 0x401, ~0x60, 0x60); + + /* Don't write into audio registers on cx2583x chips */ + if (is_cx2583x(state)) + return; + + cx25840_and_or(client, 0x810, ~0x01, 1); + + if (state->radio) { + cx25840_write(client, 0x808, 0xf9); + cx25840_write(client, 0x80b, 0x00); + } + else if (std & V4L2_STD_525_60) { + /* Certain Hauppauge PVR150 models have a hardware bug + that causes audio to drop out. For these models the + audio standard must be set explicitly. + To be precise: it affects cards with tuner models + 85, 99 and 112 (model numbers from tveeprom). */ + int hw_fix = state->pvr150_workaround; + + if (std == V4L2_STD_NTSC_M_JP) { + /* Japan uses EIAJ audio standard */ + cx25840_write(client, 0x808, hw_fix ? 0x2f : 0xf7); + } else if (std == V4L2_STD_NTSC_M_KR) { + /* South Korea uses A2 audio standard */ + cx25840_write(client, 0x808, hw_fix ? 0x3f : 0xf8); + } else { + /* Others use the BTSC audio standard */ + cx25840_write(client, 0x808, hw_fix ? 0x1f : 0xf6); + } + cx25840_write(client, 0x80b, 0x00); + } else if (std & V4L2_STD_PAL) { + /* Autodetect audio standard and audio system */ + cx25840_write(client, 0x808, 0xff); + /* Since system PAL-L is pretty much non-existent and + not used by any public broadcast network, force + 6.5 MHz carrier to be interpreted as System DK, + this avoids DK audio detection instability */ + cx25840_write(client, 0x80b, 0x00); + } else if (std & V4L2_STD_SECAM) { + /* Autodetect audio standard and audio system */ + cx25840_write(client, 0x808, 0xff); + /* If only one of SECAM-DK / SECAM-L is required, then force + 6.5MHz carrier, else autodetect it */ + if ((std & V4L2_STD_SECAM_DK) && + !(std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) { + /* 6.5 MHz carrier to be interpreted as System DK */ + cx25840_write(client, 0x80b, 0x00); + } else if (!(std & V4L2_STD_SECAM_DK) && + (std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) { + /* 6.5 MHz carrier to be interpreted as System L */ + cx25840_write(client, 0x80b, 0x08); + } else { + /* 6.5 MHz carrier to be autodetected */ + cx25840_write(client, 0x80b, 0x10); + } + } + + cx25840_and_or(client, 0x810, ~0x01, 0); +} + +static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input, + enum cx25840_audio_input aud_input) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + u8 is_composite = (vid_input >= CX25840_COMPOSITE1 && + vid_input <= CX25840_COMPOSITE8); + u8 is_component = (vid_input & CX25840_COMPONENT_ON) == + CX25840_COMPONENT_ON; + u8 is_dif = (vid_input & CX25840_DIF_ON) == + CX25840_DIF_ON; + u8 is_svideo = (vid_input & CX25840_SVIDEO_ON) == + CX25840_SVIDEO_ON; + int luma = vid_input & 0xf0; + int chroma = vid_input & 0xf00; + u8 reg; + u32 val; + + v4l_dbg(1, cx25840_debug, client, + "decoder set video input %d, audio input %d\n", + vid_input, aud_input); + + if (vid_input >= CX25840_VIN1_CH1) { + v4l_dbg(1, cx25840_debug, client, "vid_input 0x%x\n", + vid_input); + reg = vid_input & 0xff; + is_composite = !is_component && + ((vid_input & CX25840_SVIDEO_ON) != CX25840_SVIDEO_ON); + + v4l_dbg(1, cx25840_debug, client, "mux cfg 0x%x comp=%d\n", + reg, is_composite); + } else if (is_composite) { + reg = 0xf0 + (vid_input - CX25840_COMPOSITE1); + } else { + if ((vid_input & ~0xff0) || + luma < CX25840_SVIDEO_LUMA1 || luma > CX25840_SVIDEO_LUMA8 || + chroma < CX25840_SVIDEO_CHROMA4 || chroma > CX25840_SVIDEO_CHROMA8) { + v4l_err(client, "0x%04x is not a valid video input!\n", + vid_input); + return -EINVAL; + } + reg = 0xf0 + ((luma - CX25840_SVIDEO_LUMA1) >> 4); + if (chroma >= CX25840_SVIDEO_CHROMA7) { + reg &= 0x3f; + reg |= (chroma - CX25840_SVIDEO_CHROMA7) >> 2; + } else { + reg &= 0xcf; + reg |= (chroma - CX25840_SVIDEO_CHROMA4) >> 4; + } + } + + /* The caller has previously prepared the correct routing + * configuration in reg (for the cx23885) so we have no + * need to attempt to flip bits for earlier av decoders. + */ + if (!is_cx2388x(state) && !is_cx231xx(state)) { + switch (aud_input) { + case CX25840_AUDIO_SERIAL: + /* do nothing, use serial audio input */ + break; + case CX25840_AUDIO4: reg &= ~0x30; break; + case CX25840_AUDIO5: reg &= ~0x30; reg |= 0x10; break; + case CX25840_AUDIO6: reg &= ~0x30; reg |= 0x20; break; + case CX25840_AUDIO7: reg &= ~0xc0; break; + case CX25840_AUDIO8: reg &= ~0xc0; reg |= 0x40; break; + + default: + v4l_err(client, "0x%04x is not a valid audio input!\n", + aud_input); + return -EINVAL; + } + } + + cx25840_write(client, 0x103, reg); + + /* Set INPUT_MODE to Composite, S-Video or Component */ + if (is_component) + cx25840_and_or(client, 0x401, ~0x6, 0x6); + else + cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02); + + if (is_cx2388x(state)) { + + /* Enable or disable the DIF for tuner use */ + if (is_dif) { + cx25840_and_or(client, 0x102, ~0x80, 0x80); + + /* Set of defaults for NTSC and PAL */ + cx25840_write4(client, 0x31c, 0xc2262600); + cx25840_write4(client, 0x320, 0xc2262600); + + /* 18271 IF - Nobody else yet uses a different + * tuner with the DIF, so these are reasonable + * assumptions (HVR1250 and HVR1850 specific). + */ + cx25840_write4(client, 0x318, 0xda262600); + cx25840_write4(client, 0x33c, 0x2a24c800); + cx25840_write4(client, 0x104, 0x0704dd00); + } else { + cx25840_write4(client, 0x300, 0x015c28f5); + + cx25840_and_or(client, 0x102, ~0x80, 0); + cx25840_write4(client, 0x340, 0xdf7df83); + cx25840_write4(client, 0x104, 0x0704dd80); + cx25840_write4(client, 0x314, 0x22400600); + cx25840_write4(client, 0x318, 0x40002600); + cx25840_write4(client, 0x324, 0x40002600); + cx25840_write4(client, 0x32c, 0x0250e620); + cx25840_write4(client, 0x39c, 0x01FF0B00); + + cx25840_write4(client, 0x410, 0xffff0dbf); + cx25840_write4(client, 0x414, 0x00137d03); + + /* on the 887, 0x418 is HSCALE_CTRL, on the 888 it is + CHROMA_CTRL */ + if (is_cx23888(state)) + cx25840_write4(client, 0x418, 0x01008080); + else + cx25840_write4(client, 0x418, 0x01000000); + + cx25840_write4(client, 0x41c, 0x00000000); + + /* on the 887, 0x420 is CHROMA_CTRL, on the 888 it is + CRUSH_CTRL */ + if (is_cx23888(state)) + cx25840_write4(client, 0x420, 0x001c3e0f); + else + cx25840_write4(client, 0x420, 0x001c8282); + + cx25840_write4(client, 0x42c, 0x42600000); + cx25840_write4(client, 0x430, 0x0000039b); + cx25840_write4(client, 0x438, 0x00000000); + + cx25840_write4(client, 0x440, 0xF8E3E824); + cx25840_write4(client, 0x444, 0x401040dc); + cx25840_write4(client, 0x448, 0xcd3f02a0); + cx25840_write4(client, 0x44c, 0x161f1000); + cx25840_write4(client, 0x450, 0x00000802); + + cx25840_write4(client, 0x91c, 0x01000000); + cx25840_write4(client, 0x8e0, 0x03063870); + cx25840_write4(client, 0x8d4, 0x7FFF0024); + cx25840_write4(client, 0x8d0, 0x00063073); + + cx25840_write4(client, 0x8c8, 0x00010000); + cx25840_write4(client, 0x8cc, 0x00080023); + + /* DIF BYPASS */ + cx25840_write4(client, 0x33c, 0x2a04c800); + } + + /* Reset the DIF */ + cx25840_write4(client, 0x398, 0); + } + + if (!is_cx2388x(state) && !is_cx231xx(state)) { + /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ + cx25840_and_or(client, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0); + /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2&CH3 */ + if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30) + cx25840_and_or(client, 0x102, ~0x4, 4); + else + cx25840_and_or(client, 0x102, ~0x4, 0); + } else { + /* Set DUAL_MODE_ADC2 to 1 if component*/ + cx25840_and_or(client, 0x102, ~0x4, is_component ? 0x4 : 0x0); + if (is_composite) { + /* ADC2 input select channel 2 */ + cx25840_and_or(client, 0x102, ~0x2, 0); + } else if (!is_component) { + /* S-Video */ + if (chroma >= CX25840_SVIDEO_CHROMA7) { + /* ADC2 input select channel 3 */ + cx25840_and_or(client, 0x102, ~0x2, 2); + } else { + /* ADC2 input select channel 2 */ + cx25840_and_or(client, 0x102, ~0x2, 0); + } + } + + /* cx23885 / SVIDEO */ + if (is_cx2388x(state) && is_svideo) { +#define AFE_CTRL (0x104) +#define MODE_CTRL (0x400) + cx25840_and_or(client, 0x102, ~0x2, 0x2); + + val = cx25840_read4(client, MODE_CTRL); + val &= 0xFFFFF9FF; + + /* YC */ + val |= 0x00000200; + val &= ~0x2000; + cx25840_write4(client, MODE_CTRL, val); + + val = cx25840_read4(client, AFE_CTRL); + + /* Chroma in select */ + val |= 0x00001000; + val &= 0xfffffe7f; + /* Clear VGA_SEL_CH2 and VGA_SEL_CH3 (bits 7 and 8). + * This sets them to use video rather than audio. + * Only one of the two will be in use. + */ + cx25840_write4(client, AFE_CTRL, val); + } else + cx25840_and_or(client, 0x102, ~0x2, 0); + } + + state->vid_input = vid_input; + state->aud_input = aud_input; + cx25840_audio_set_path(client); + input_change(client); + + if (is_cx2388x(state)) { + /* Audio channel 1 src : Parallel 1 */ + cx25840_write(client, 0x124, 0x03); + + /* Select AFE clock pad output source */ + cx25840_write(client, 0x144, 0x05); + + /* I2S_IN_CTL: I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 */ + cx25840_write(client, 0x914, 0xa0); + + /* I2S_OUT_CTL: + * I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 + * I2S_OUT_MASTER_MODE = Master + */ + cx25840_write(client, 0x918, 0xa0); + cx25840_write(client, 0x919, 0x01); + } else if (is_cx231xx(state)) { + /* Audio channel 1 src : Parallel 1 */ + cx25840_write(client, 0x124, 0x03); + + /* I2S_IN_CTL: I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 */ + cx25840_write(client, 0x914, 0xa0); + + /* I2S_OUT_CTL: + * I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 + * I2S_OUT_MASTER_MODE = Master + */ + cx25840_write(client, 0x918, 0xa0); + cx25840_write(client, 0x919, 0x01); + } + + if (is_cx2388x(state) && ((aud_input == CX25840_AUDIO7) || + (aud_input == CX25840_AUDIO6))) { + /* Configure audio from LR1 or LR2 input */ + cx25840_write4(client, 0x910, 0); + cx25840_write4(client, 0x8d0, 0x63073); + } else + if (is_cx2388x(state) && (aud_input == CX25840_AUDIO8)) { + /* Configure audio from tuner/sif input */ + cx25840_write4(client, 0x910, 0x12b000c9); + cx25840_write4(client, 0x8d0, 0x1f063870); + } + + if (is_cx23888(state)) { + /* HVR1850 */ + /* AUD_IO_CTRL - I2S Input, Parallel1*/ + /* - Channel 1 src - Parallel1 (Merlin out) */ + /* - Channel 2 src - Parallel2 (Merlin out) */ + /* - Channel 3 src - Parallel3 (Merlin AC97 out) */ + /* - I2S source and dir - Merlin, output */ + cx25840_write4(client, 0x124, 0x100); + + if (!is_dif) { + /* Stop microcontroller if we don't need it + * to avoid audio popping on svideo/composite use. + */ + cx25840_and_or(client, 0x803, ~0x10, 0x00); + } + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int set_v4lstd(struct i2c_client *client) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + u8 fmt = 0; /* zero is autodetect */ + u8 pal_m = 0; + + /* First tests should be against specific std */ + if (state->std == V4L2_STD_NTSC_M_JP) { + fmt = 0x2; + } else if (state->std == V4L2_STD_NTSC_443) { + fmt = 0x3; + } else if (state->std == V4L2_STD_PAL_M) { + pal_m = 1; + fmt = 0x5; + } else if (state->std == V4L2_STD_PAL_N) { + fmt = 0x6; + } else if (state->std == V4L2_STD_PAL_Nc) { + fmt = 0x7; + } else if (state->std == V4L2_STD_PAL_60) { + fmt = 0x8; + } else { + /* Then, test against generic ones */ + if (state->std & V4L2_STD_NTSC) + fmt = 0x1; + else if (state->std & V4L2_STD_PAL) + fmt = 0x4; + else if (state->std & V4L2_STD_SECAM) + fmt = 0xc; + } + + v4l_dbg(1, cx25840_debug, client, "changing video std to fmt %i\n",fmt); + + /* Follow step 9 of section 3.16 in the cx25840 datasheet. + Without this PAL may display a vertical ghosting effect. + This happens for example with the Yuan MPC622. */ + if (fmt >= 4 && fmt < 8) { + /* Set format to NTSC-M */ + cx25840_and_or(client, 0x400, ~0xf, 1); + /* Turn off LCOMB */ + cx25840_and_or(client, 0x47b, ~6, 0); + } + cx25840_and_or(client, 0x400, ~0xf, fmt); + cx25840_and_or(client, 0x403, ~0x3, pal_m); + if (is_cx23888(state)) + cx23888_std_setup(client); + else + cx25840_std_setup(client); + if (!is_cx2583x(state)) + input_change(client); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int cx25840_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + cx25840_write(client, 0x414, ctrl->val - 128); + break; + + case V4L2_CID_CONTRAST: + cx25840_write(client, 0x415, ctrl->val << 1); + break; + + case V4L2_CID_SATURATION: + if (is_cx23888(state)) { + cx25840_write(client, 0x418, ctrl->val << 1); + cx25840_write(client, 0x419, ctrl->val << 1); + } else { + cx25840_write(client, 0x420, ctrl->val << 1); + cx25840_write(client, 0x421, ctrl->val << 1); + } + break; + + case V4L2_CID_HUE: + if (is_cx23888(state)) + cx25840_write(client, 0x41a, ctrl->val); + else + cx25840_write(client, 0x422, ctrl->val); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int cx25840_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int HSC, VSC, Vsrc, Hsrc, filter, Vlines; + int is_50Hz = !(state->std & V4L2_STD_525_60); + + if (fmt->code != V4L2_MBUS_FMT_FIXED) + return -EINVAL; + + fmt->field = V4L2_FIELD_INTERLACED; + fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + + if (is_cx23888(state)) { + Vsrc = (cx25840_read(client, 0x42a) & 0x3f) << 4; + Vsrc |= (cx25840_read(client, 0x429) & 0xf0) >> 4; + } else { + Vsrc = (cx25840_read(client, 0x476) & 0x3f) << 4; + Vsrc |= (cx25840_read(client, 0x475) & 0xf0) >> 4; + } + + if (is_cx23888(state)) { + Hsrc = (cx25840_read(client, 0x426) & 0x3f) << 4; + Hsrc |= (cx25840_read(client, 0x425) & 0xf0) >> 4; + } else { + Hsrc = (cx25840_read(client, 0x472) & 0x3f) << 4; + Hsrc |= (cx25840_read(client, 0x471) & 0xf0) >> 4; + } + + Vlines = fmt->height + (is_50Hz ? 4 : 7); + + if ((fmt->width * 16 < Hsrc) || (Hsrc < fmt->width) || + (Vlines * 8 < Vsrc) || (Vsrc < Vlines)) { + v4l_err(client, "%dx%d is not a valid size!\n", + fmt->width, fmt->height); + return -ERANGE; + } + + HSC = (Hsrc * (1 << 20)) / fmt->width - (1 << 20); + VSC = (1 << 16) - (Vsrc * (1 << 9) / Vlines - (1 << 9)); + VSC &= 0x1fff; + + if (fmt->width >= 385) + filter = 0; + else if (fmt->width > 192) + filter = 1; + else if (fmt->width > 96) + filter = 2; + else + filter = 3; + + v4l_dbg(1, cx25840_debug, client, "decoder set size %dx%d -> scale %ux%u\n", + fmt->width, fmt->height, HSC, VSC); + + /* HSCALE=HSC */ + cx25840_write(client, 0x418, HSC & 0xff); + cx25840_write(client, 0x419, (HSC >> 8) & 0xff); + cx25840_write(client, 0x41a, HSC >> 16); + /* VSCALE=VSC */ + cx25840_write(client, 0x41c, VSC & 0xff); + cx25840_write(client, 0x41d, VSC >> 8); + /* VS_INTRLACE=1 VFILT=filter */ + cx25840_write(client, 0x41e, 0x8 | filter); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static void log_video_status(struct i2c_client *client) +{ + static const char *const fmt_strs[] = { + "0x0", + "NTSC-M", "NTSC-J", "NTSC-4.43", + "PAL-BDGHI", "PAL-M", "PAL-N", "PAL-Nc", "PAL-60", + "0x9", "0xA", "0xB", + "SECAM", + "0xD", "0xE", "0xF" + }; + + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + u8 vidfmt_sel = cx25840_read(client, 0x400) & 0xf; + u8 gen_stat1 = cx25840_read(client, 0x40d); + u8 gen_stat2 = cx25840_read(client, 0x40e); + int vid_input = state->vid_input; + + v4l_info(client, "Video signal: %spresent\n", + (gen_stat2 & 0x20) ? "" : "not "); + v4l_info(client, "Detected format: %s\n", + fmt_strs[gen_stat1 & 0xf]); + + v4l_info(client, "Specified standard: %s\n", + vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection"); + + if (vid_input >= CX25840_COMPOSITE1 && + vid_input <= CX25840_COMPOSITE8) { + v4l_info(client, "Specified video input: Composite %d\n", + vid_input - CX25840_COMPOSITE1 + 1); + } else { + v4l_info(client, "Specified video input: S-Video (Luma In%d, Chroma In%d)\n", + (vid_input & 0xf0) >> 4, (vid_input & 0xf00) >> 8); + } + + v4l_info(client, "Specified audioclock freq: %d Hz\n", state->audclk_freq); +} + +/* ----------------------------------------------------------------------- */ + +static void log_audio_status(struct i2c_client *client) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + u8 download_ctl = cx25840_read(client, 0x803); + u8 mod_det_stat0 = cx25840_read(client, 0x804); + u8 mod_det_stat1 = cx25840_read(client, 0x805); + u8 audio_config = cx25840_read(client, 0x808); + u8 pref_mode = cx25840_read(client, 0x809); + u8 afc0 = cx25840_read(client, 0x80b); + u8 mute_ctl = cx25840_read(client, 0x8d3); + int aud_input = state->aud_input; + char *p; + + switch (mod_det_stat0) { + case 0x00: p = "mono"; break; + case 0x01: p = "stereo"; break; + case 0x02: p = "dual"; break; + case 0x04: p = "tri"; break; + case 0x10: p = "mono with SAP"; break; + case 0x11: p = "stereo with SAP"; break; + case 0x12: p = "dual with SAP"; break; + case 0x14: p = "tri with SAP"; break; + case 0xfe: p = "forced mode"; break; + default: p = "not defined"; + } + v4l_info(client, "Detected audio mode: %s\n", p); + + switch (mod_det_stat1) { + case 0x00: p = "not defined"; break; + case 0x01: p = "EIAJ"; break; + case 0x02: p = "A2-M"; break; + case 0x03: p = "A2-BG"; break; + case 0x04: p = "A2-DK1"; break; + case 0x05: p = "A2-DK2"; break; + case 0x06: p = "A2-DK3"; break; + case 0x07: p = "A1 (6.0 MHz FM Mono)"; break; + case 0x08: p = "AM-L"; break; + case 0x09: p = "NICAM-BG"; break; + case 0x0a: p = "NICAM-DK"; break; + case 0x0b: p = "NICAM-I"; break; + case 0x0c: p = "NICAM-L"; break; + case 0x0d: p = "BTSC/EIAJ/A2-M Mono (4.5 MHz FMMono)"; break; + case 0x0e: p = "IF FM Radio"; break; + case 0x0f: p = "BTSC"; break; + case 0x10: p = "high-deviation FM"; break; + case 0x11: p = "very high-deviation FM"; break; + case 0xfd: p = "unknown audio standard"; break; + case 0xfe: p = "forced audio standard"; break; + case 0xff: p = "no detected audio standard"; break; + default: p = "not defined"; + } + v4l_info(client, "Detected audio standard: %s\n", p); + v4l_info(client, "Audio microcontroller: %s\n", + (download_ctl & 0x10) ? + ((mute_ctl & 0x2) ? "detecting" : "running") : "stopped"); + + switch (audio_config >> 4) { + case 0x00: p = "undefined"; break; + case 0x01: p = "BTSC"; break; + case 0x02: p = "EIAJ"; break; + case 0x03: p = "A2-M"; break; + case 0x04: p = "A2-BG"; break; + case 0x05: p = "A2-DK1"; break; + case 0x06: p = "A2-DK2"; break; + case 0x07: p = "A2-DK3"; break; + case 0x08: p = "A1 (6.0 MHz FM Mono)"; break; + case 0x09: p = "AM-L"; break; + case 0x0a: p = "NICAM-BG"; break; + case 0x0b: p = "NICAM-DK"; break; + case 0x0c: p = "NICAM-I"; break; + case 0x0d: p = "NICAM-L"; break; + case 0x0e: p = "FM radio"; break; + case 0x0f: p = "automatic detection"; break; + default: p = "undefined"; + } + v4l_info(client, "Configured audio standard: %s\n", p); + + if ((audio_config >> 4) < 0xF) { + switch (audio_config & 0xF) { + case 0x00: p = "MONO1 (LANGUAGE A/Mono L+R channel for BTSC, EIAJ, A2)"; break; + case 0x01: p = "MONO2 (LANGUAGE B)"; break; + case 0x02: p = "MONO3 (STEREO forced MONO)"; break; + case 0x03: p = "MONO4 (NICAM ANALOG-Language C/Analog Fallback)"; break; + case 0x04: p = "STEREO"; break; + case 0x05: p = "DUAL1 (AB)"; break; + case 0x06: p = "DUAL2 (AC) (FM)"; break; + case 0x07: p = "DUAL3 (BC) (FM)"; break; + case 0x08: p = "DUAL4 (AC) (AM)"; break; + case 0x09: p = "DUAL5 (BC) (AM)"; break; + case 0x0a: p = "SAP"; break; + default: p = "undefined"; + } + v4l_info(client, "Configured audio mode: %s\n", p); + } else { + switch (audio_config & 0xF) { + case 0x00: p = "BG"; break; + case 0x01: p = "DK1"; break; + case 0x02: p = "DK2"; break; + case 0x03: p = "DK3"; break; + case 0x04: p = "I"; break; + case 0x05: p = "L"; break; + case 0x06: p = "BTSC"; break; + case 0x07: p = "EIAJ"; break; + case 0x08: p = "A2-M"; break; + case 0x09: p = "FM Radio"; break; + case 0x0f: p = "automatic standard and mode detection"; break; + default: p = "undefined"; + } + v4l_info(client, "Configured audio system: %s\n", p); + } + + if (aud_input) { + v4l_info(client, "Specified audio input: Tuner (In%d)\n", aud_input); + } else { + v4l_info(client, "Specified audio input: External\n"); + } + + switch (pref_mode & 0xf) { + case 0: p = "mono/language A"; break; + case 1: p = "language B"; break; + case 2: p = "language C"; break; + case 3: p = "analog fallback"; break; + case 4: p = "stereo"; break; + case 5: p = "language AC"; break; + case 6: p = "language BC"; break; + case 7: p = "language AB"; break; + default: p = "undefined"; + } + v4l_info(client, "Preferred audio mode: %s\n", p); + + if ((audio_config & 0xf) == 0xf) { + switch ((afc0 >> 3) & 0x3) { + case 0: p = "system DK"; break; + case 1: p = "system L"; break; + case 2: p = "autodetect"; break; + default: p = "undefined"; + } + v4l_info(client, "Selected 65 MHz format: %s\n", p); + + switch (afc0 & 0x7) { + case 0: p = "chroma"; break; + case 1: p = "BTSC"; break; + case 2: p = "EIAJ"; break; + case 3: p = "A2-M"; break; + case 4: p = "autodetect"; break; + default: p = "undefined"; + } + v4l_info(client, "Selected 45 MHz format: %s\n", p); + } +} + +/* ----------------------------------------------------------------------- */ + +/* This load_fw operation must be called to load the driver's firmware. + Without this the audio standard detection will fail and you will + only get mono. + + Since loading the firmware is often problematic when the driver is + compiled into the kernel I recommend postponing calling this function + until the first open of the video device. Another reason for + postponing it is that loading this firmware takes a long time (seconds) + due to the slow i2c bus speed. So it will speed up the boot process if + you can avoid loading the fw as long as the video device isn't used. */ +static int cx25840_load_fw(struct v4l2_subdev *sd) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!state->is_initialized) { + /* initialize and load firmware */ + state->is_initialized = 1; + if (is_cx2583x(state)) + cx25836_initialize(client); + else if (is_cx2388x(state)) + cx23885_initialize(client); + else if (is_cx231xx(state)) + cx231xx_initialize(client); + else + cx25840_initialize(client); + } + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int cx25840_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->size = 1; + reg->val = cx25840_read(client, reg->reg & 0x0fff); + return 0; +} + +static int cx25840_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + cx25840_write(client, reg->reg & 0x0fff, reg->val & 0xff); + return 0; +} +#endif + +static int cx25840_s_audio_stream(struct v4l2_subdev *sd, int enable) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 v; + + if (is_cx2583x(state) || is_cx2388x(state) || is_cx231xx(state)) + return 0; + + v4l_dbg(1, cx25840_debug, client, "%s audio output\n", + enable ? "enable" : "disable"); + + if (enable) { + v = cx25840_read(client, 0x115) | 0x80; + cx25840_write(client, 0x115, v); + v = cx25840_read(client, 0x116) | 0x03; + cx25840_write(client, 0x116, v); + } else { + v = cx25840_read(client, 0x115) & ~(0x80); + cx25840_write(client, 0x115, v); + v = cx25840_read(client, 0x116) & ~(0x03); + cx25840_write(client, 0x116, v); + } + return 0; +} + +static int cx25840_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 v; + + v4l_dbg(1, cx25840_debug, client, "%s video output\n", + enable ? "enable" : "disable"); + if (enable) { + if (is_cx2388x(state) || is_cx231xx(state)) { + v = cx25840_read(client, 0x421) | 0x0b; + cx25840_write(client, 0x421, v); + } else { + v = cx25840_read(client, 0x115) | 0x0c; + cx25840_write(client, 0x115, v); + v = cx25840_read(client, 0x116) | 0x04; + cx25840_write(client, 0x116, v); + } + } else { + if (is_cx2388x(state) || is_cx231xx(state)) { + v = cx25840_read(client, 0x421) & ~(0x0b); + cx25840_write(client, 0x421, v); + } else { + v = cx25840_read(client, 0x115) & ~(0x0c); + cx25840_write(client, 0x115, v); + v = cx25840_read(client, 0x116) & ~(0x04); + cx25840_write(client, 0x116, v); + } + } + return 0; +} + +/* Query the current detected video format */ +static int cx25840_g_std(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + v4l2_std_id stds[] = { + /* 0000 */ V4L2_STD_UNKNOWN, + + /* 0001 */ V4L2_STD_NTSC_M, + /* 0010 */ V4L2_STD_NTSC_M_JP, + /* 0011 */ V4L2_STD_NTSC_443, + /* 0100 */ V4L2_STD_PAL, + /* 0101 */ V4L2_STD_PAL_M, + /* 0110 */ V4L2_STD_PAL_N, + /* 0111 */ V4L2_STD_PAL_Nc, + /* 1000 */ V4L2_STD_PAL_60, + + /* 1001 */ V4L2_STD_UNKNOWN, + /* 1010 */ V4L2_STD_UNKNOWN, + /* 1001 */ V4L2_STD_UNKNOWN, + /* 1010 */ V4L2_STD_UNKNOWN, + /* 1011 */ V4L2_STD_UNKNOWN, + /* 1110 */ V4L2_STD_UNKNOWN, + /* 1111 */ V4L2_STD_UNKNOWN + }; + + u32 fmt = (cx25840_read4(client, 0x40c) >> 8) & 0xf; + *std = stds[ fmt ]; + + v4l_dbg(1, cx25840_debug, client, "g_std fmt = %x, v4l2_std_id = 0x%x\n", + fmt, (unsigned int)stds[ fmt ]); + + return 0; +} + +static int cx25840_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + /* A limited function that checks for signal status and returns + * the state. + */ + + /* Check for status of Horizontal lock (SRC lock isn't reliable) */ + if ((cx25840_read4(client, 0x40c) & 0x00010000) == 0) + *status |= V4L2_IN_ST_NO_SIGNAL; + + return 0; +} + +static int cx25840_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (state->radio == 0 && state->std == std) + return 0; + state->radio = 0; + state->std = std; + return set_v4lstd(client); +} + +static int cx25840_s_radio(struct v4l2_subdev *sd) +{ + struct cx25840_state *state = to_state(sd); + + state->radio = 1; + return 0; +} + +static int cx25840_s_video_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (is_cx23888(state)) + cx23888_std_setup(client); + + return set_input(client, input, state->aud_input); +} + +static int cx25840_s_audio_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (is_cx23888(state)) + cx23888_std_setup(client); + return set_input(client, state->vid_input, input); +} + +static int cx25840_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + input_change(client); + return 0; +} + +static int cx25840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 vpres = cx25840_read(client, 0x40e) & 0x20; + u8 mode; + int val = 0; + + if (state->radio) + return 0; + + vt->signal = vpres ? 0xffff : 0x0; + if (is_cx2583x(state)) + return 0; + + vt->capability |= + V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | + V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; + + mode = cx25840_read(client, 0x804); + + /* get rxsubchans and audmode */ + if ((mode & 0xf) == 1) + val |= V4L2_TUNER_SUB_STEREO; + else + val |= V4L2_TUNER_SUB_MONO; + + if (mode == 2 || mode == 4) + val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + + if (mode & 0x10) + val |= V4L2_TUNER_SUB_SAP; + + vt->rxsubchans = val; + vt->audmode = state->audmode; + return 0; +} + +static int cx25840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (state->radio || is_cx2583x(state)) + return 0; + + switch (vt->audmode) { + case V4L2_TUNER_MODE_MONO: + /* mono -> mono + stereo -> mono + bilingual -> lang1 */ + cx25840_and_or(client, 0x809, ~0xf, 0x00); + break; + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1: + /* mono -> mono + stereo -> stereo + bilingual -> lang1 */ + cx25840_and_or(client, 0x809, ~0xf, 0x04); + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + /* mono -> mono + stereo -> stereo + bilingual -> lang1/lang2 */ + cx25840_and_or(client, 0x809, ~0xf, 0x07); + break; + case V4L2_TUNER_MODE_LANG2: + /* mono -> mono + stereo -> stereo + bilingual -> lang2 */ + cx25840_and_or(client, 0x809, ~0xf, 0x01); + break; + default: + return -EINVAL; + } + state->audmode = vt->audmode; + return 0; +} + +static int cx25840_reset(struct v4l2_subdev *sd, u32 val) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (is_cx2583x(state)) + cx25836_initialize(client); + else if (is_cx2388x(state)) + cx23885_initialize(client); + else if (is_cx231xx(state)) + cx231xx_initialize(client); + else + cx25840_initialize(client); + return 0; +} + +static int cx25840_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, state->id, state->rev); +} + +static int cx25840_log_status(struct v4l2_subdev *sd) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + log_video_status(client); + if (!is_cx2583x(state)) + log_audio_status(client); + cx25840_ir_log_status(sd); + v4l2_ctrl_handler_log_status(&state->hdl, sd->name); + return 0; +} + +static int cx23885_irq_handler(struct v4l2_subdev *sd, u32 status, + bool *handled) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *c = v4l2_get_subdevdata(sd); + u8 irq_stat, aud_stat, aud_en, ir_stat, ir_en; + u32 vid_stat, aud_mc_stat; + bool block_handled; + int ret = 0; + + irq_stat = cx25840_read(c, CX23885_PIN_CTRL_IRQ_REG); + v4l_dbg(2, cx25840_debug, c, "AV Core IRQ status (entry): %s %s %s\n", + irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT ? "ir" : " ", + irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT ? "aud" : " ", + irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT ? "vid" : " "); + + if ((is_cx23885(state) || is_cx23887(state))) { + ir_stat = cx25840_read(c, CX25840_IR_STATS_REG); + ir_en = cx25840_read(c, CX25840_IR_IRQEN_REG); + v4l_dbg(2, cx25840_debug, c, + "AV Core ir IRQ status: %#04x disables: %#04x\n", + ir_stat, ir_en); + if (irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT) { + block_handled = false; + ret = cx25840_ir_irq_handler(sd, + status, &block_handled); + if (block_handled) + *handled = true; + } + } + + aud_stat = cx25840_read(c, CX25840_AUD_INT_STAT_REG); + aud_en = cx25840_read(c, CX25840_AUD_INT_CTRL_REG); + v4l_dbg(2, cx25840_debug, c, + "AV Core audio IRQ status: %#04x disables: %#04x\n", + aud_stat, aud_en); + aud_mc_stat = cx25840_read4(c, CX23885_AUD_MC_INT_MASK_REG); + v4l_dbg(2, cx25840_debug, c, + "AV Core audio MC IRQ status: %#06x enables: %#06x\n", + aud_mc_stat >> CX23885_AUD_MC_INT_STAT_SHFT, + aud_mc_stat & CX23885_AUD_MC_INT_CTRL_BITS); + if (irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT) { + if (aud_stat) { + cx25840_write(c, CX25840_AUD_INT_STAT_REG, aud_stat); + *handled = true; + } + } + + vid_stat = cx25840_read4(c, CX25840_VID_INT_STAT_REG); + v4l_dbg(2, cx25840_debug, c, + "AV Core video IRQ status: %#06x disables: %#06x\n", + vid_stat & CX25840_VID_INT_STAT_BITS, + vid_stat >> CX25840_VID_INT_MASK_SHFT); + if (irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT) { + if (vid_stat & CX25840_VID_INT_STAT_BITS) { + cx25840_write4(c, CX25840_VID_INT_STAT_REG, vid_stat); + *handled = true; + } + } + + irq_stat = cx25840_read(c, CX23885_PIN_CTRL_IRQ_REG); + v4l_dbg(2, cx25840_debug, c, "AV Core IRQ status (exit): %s %s %s\n", + irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT ? "ir" : " ", + irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT ? "aud" : " ", + irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT ? "vid" : " "); + + return ret; +} + +static int cx25840_irq_handler(struct v4l2_subdev *sd, u32 status, + bool *handled) +{ + struct cx25840_state *state = to_state(sd); + + *handled = false; + + /* Only support the CX2388[578] AV Core for now */ + if (is_cx2388x(state)) + return cx23885_irq_handler(sd, status, handled); + + return -ENODEV; +} + +/* ----------------------------------------------------------------------- */ + +#define DIF_PLL_FREQ_WORD (0x300) +#define DIF_BPF_COEFF01 (0x348) +#define DIF_BPF_COEFF23 (0x34c) +#define DIF_BPF_COEFF45 (0x350) +#define DIF_BPF_COEFF67 (0x354) +#define DIF_BPF_COEFF89 (0x358) +#define DIF_BPF_COEFF1011 (0x35c) +#define DIF_BPF_COEFF1213 (0x360) +#define DIF_BPF_COEFF1415 (0x364) +#define DIF_BPF_COEFF1617 (0x368) +#define DIF_BPF_COEFF1819 (0x36c) +#define DIF_BPF_COEFF2021 (0x370) +#define DIF_BPF_COEFF2223 (0x374) +#define DIF_BPF_COEFF2425 (0x378) +#define DIF_BPF_COEFF2627 (0x37c) +#define DIF_BPF_COEFF2829 (0x380) +#define DIF_BPF_COEFF3031 (0x384) +#define DIF_BPF_COEFF3233 (0x388) +#define DIF_BPF_COEFF3435 (0x38c) +#define DIF_BPF_COEFF36 (0x390) + +void cx23885_dif_setup(struct i2c_client *client, u32 ifHz) +{ + u64 pll_freq; + u32 pll_freq_word; + + v4l_dbg(1, cx25840_debug, client, "%s(%d)\n", __func__, ifHz); + + /* Assuming TV */ + /* Calculate the PLL frequency word based on the adjusted ifHz */ + pll_freq = div_u64((u64)ifHz * 268435456, 50000000); + pll_freq_word = (u32)pll_freq; + + cx25840_write4(client, DIF_PLL_FREQ_WORD, pll_freq_word); + + /* Round down to the nearest 100KHz */ + ifHz = (ifHz / 100000) * 100000; + + if (ifHz < 3000000) + ifHz = 3000000; + + if (ifHz > 16000000) + ifHz = 16000000; + + v4l_dbg(1, cx25840_debug, client, "%s(%d) again\n", __func__, ifHz); + + switch (ifHz) { + case 3000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00080012); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001e0024); + cx25840_write4(client, DIF_BPF_COEFF67, 0x001bfff8); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffb4ff50); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed8fe68); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe24fe34); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfebaffc7); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d031f); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x04f0065d); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x07010688); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x04c901d6); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe00f9d3); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600f342); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf235f337); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf64efb22); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0105070f); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x0c460fce); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 3100000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00070012); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00220032); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00370026); + cx25840_write4(client, DIF_BPF_COEFF89, 0xfff0ff91); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff0efe7c); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe01fdcc); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe0afedb); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x00440224); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0434060c); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0738074e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x06090361); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xff99fb39); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fef3b6); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf21af2a5); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf573fa33); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0034067d); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x0bfb0fb9); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 3200000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000000); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0004000e); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00200038); + cx25840_write4(client, DIF_BPF_COEFF67, 0x004c004f); + cx25840_write4(client, DIF_BPF_COEFF89, 0x002fffdf); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff5cfeb6); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe0dfd92); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd7ffe03); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36010a); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x03410575); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x072607d2); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x071804d5); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0134fcb7); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81ff451); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf223f22e); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf4a7f94b); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xff6405e8); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x0bae0fa4); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 3300000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00000008); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001a0036); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0056006d); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00670030); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xffbdff10); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe46fd8d); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd25fd4f); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35ffe0); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0224049f); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x06c9080e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x07ef0627); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x02c9fe45); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf961f513); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf250f1d2); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf3ecf869); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfe930552); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x0b5f0f8f); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 3400000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffd0001); + cx25840_write4(client, DIF_BPF_COEFF45, 0x000f002c); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0054007d); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0093007c); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0024ff82); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfea6fdbb); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd03fcca); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51feb9); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x00eb0392); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x06270802); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x08880750); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x044dffdb); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabdf5f8); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2a0f193); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf342f78f); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfdc404b9); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x0b0e0f78); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 3500000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffafff9); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0002001b); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0046007d); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00ad00ba); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00870000); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xff26fe1a); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd1bfc7e); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fda4); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xffa5025c); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x054507ad); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x08dd0847); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x05b80172); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2ef6ff); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf313f170); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf2abf6bd); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfcf6041f); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x0abc0f61); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 3600000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff3); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfff50006); + cx25840_write4(client, DIF_BPF_COEFF67, 0x002f006c); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00b200e3); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00dc007e); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xffb9fea0); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd6bfc71); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fcb1); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe65010b); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x042d0713); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x08ec0906); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x07020302); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaff823); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3a7f16a); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf228f5f5); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfc2a0384); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x0a670f4a); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 3700000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff7ffef); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe9fff1); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0010004d); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00a100f2); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x011a00f0); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x0053ff44); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdedfca2); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fbef); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd39ffae); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x02ea0638); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x08b50987); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x08230483); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xff39f960); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf45bf180); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf1b8f537); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfb6102e7); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x0a110f32); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 3800000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9ffee); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe1ffdd); + cx25840_write4(client, DIF_BPF_COEFF67, 0xfff00024); + cx25840_write4(client, DIF_BPF_COEFF89, 0x007c00e5); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x013a014a); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x00e6fff8); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe98fd0f); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fb67); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc32fe54); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x01880525); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x083909c7); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x091505ee); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c7fab3); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf52df1b4); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf15df484); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfa9b0249); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x09ba0f19); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 3900000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000000); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffbfff0); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffcf); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffd1fff6); + cx25840_write4(client, DIF_BPF_COEFF89, 0x004800be); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x01390184); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x016300ac); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xff5efdb1); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fb23); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb5cfd0d); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x001703e4); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x077b09c4); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x09d2073c); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0251fc18); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf61cf203); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf118f3dc); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf9d801aa); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x09600eff); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 4000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffefff4); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe1ffc8); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffbaffca); + cx25840_write4(client, DIF_BPF_COEFF89, 0x000b0082); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x01170198); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01c10152); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0030fe7b); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fb24); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfac3fbe9); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfea5027f); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x0683097f); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a560867); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d2fd89); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf723f26f); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0e8f341); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf919010a); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x09060ee5); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 4100000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0002fffb); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe8ffca); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffacffa4); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffcd0036); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00d70184); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01f601dc); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x00ffff60); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fb6d); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa6efaf5); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfd410103); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x055708f9); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a9e0969); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0543ff02); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf842f2f5); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0cef2b2); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf85e006b); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x08aa0ecb); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 4200000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00050003); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfff3ffd3); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffaaff8b); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff95ffe5); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0080014a); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01fe023f); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x01ba0050); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fbf8); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa62fa3b); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfbf9ff7e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x04010836); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0aa90a3d); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x069f007f); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf975f395); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0cbf231); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf7a9ffcb); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x084c0eaf); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 4300000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0008000a); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0000ffe4); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffb4ff81); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff6aff96); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x001c00f0); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01d70271); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0254013b); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fcbd); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa9ff9c5); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfadbfdfe); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x028c073b); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a750adf); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e101fa); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfab8f44e); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0ddf1be); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf6f9ff2b); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x07ed0e94); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 4400000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0009000f); + cx25840_write4(client, DIF_BPF_COEFF45, 0x000efff8); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9ff87); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff52ff54); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xffb5007e); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01860270); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02c00210); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fdb2); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb22f997); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9f2fc90); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x0102060f); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a050b4c); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0902036e); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfc0af51e); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf106f15a); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf64efe8b); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x078d0e77); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 4500000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00080012); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0019000e); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5ff9e); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff4fff25); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff560000); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x0112023b); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f702c0); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfec8); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfbe5f9b3); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf947fb41); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xff7004b9); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x095a0b81); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a0004d8); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfd65f603); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf144f104); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf5aafdec); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x072b0e5a); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 4600000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00060012); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00200022); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0005ffc1); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff61ff10); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff09ff82); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x008601d7); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f50340); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fff0); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfcddfa19); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8e2fa1e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfde30343); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x08790b7f); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad50631); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfec7f6fc); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf198f0bd); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf50dfd4e); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x06c90e3d); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 4700000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0003000f); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00220030); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0025ffed); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff87ff15); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed6ff10); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xffed014c); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02b90386); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x03110119); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfdfefac4); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8c6f92f); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfc6701b7); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x07670b44); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7e0776); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x002df807); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf200f086); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf477fcb1); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x06650e1e); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 4800000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0xffff0009); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001e0038); + cx25840_write4(client, DIF_BPF_COEFF67, 0x003f001b); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffbcff36); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec2feb6); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xff5600a5); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0248038d); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b00232); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xff39fbab); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8f4f87f); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb060020); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x062a0ad2); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf908a3); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0192f922); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf27df05e); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf3e8fc14); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x06000e00); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 4900000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffc0002); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00160037); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00510046); + cx25840_write4(client, DIF_BPF_COEFF89, 0xfff9ff6d); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed0fe7c); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfecefff0); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x01aa0356); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413032b); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x007ffcc5); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf96cf812); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf9cefe87); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x04c90a2c); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c4309b4); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x02f3fa4a); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf30ef046); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf361fb7a); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x059b0de0); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 5000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fffa); + cx25840_write4(client, DIF_BPF_COEFF45, 0x000a002d); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00570067); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0037ffb5); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfefffe68); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe62ff3d); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x00ec02e3); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x043503f6); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x01befe05); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa27f7ee); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf8c6fcf8); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x034c0954); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c5c0aa4); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x044cfb7e); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf3b1f03f); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf2e2fae1); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x05340dc0); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 5100000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff4); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfffd001e); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0051007b); + cx25840_write4(client, DIF_BPF_COEFF89, 0x006e0006); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff48fe7c); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe1bfe9a); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x001d023e); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x04130488); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x02e6ff5b); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfb1ef812); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7f7fb7f); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x01bc084e); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c430b72); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x059afcba); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf467f046); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf26cfa4a); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x04cd0da0); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 5200000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8ffef); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfff00009); + cx25840_write4(client, DIF_BPF_COEFF67, 0x003f007f); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00980056); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xffa5feb6); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe00fe15); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xff4b0170); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b004d7); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x03e800b9); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfc48f87f); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf768fa23); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0022071f); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf90c1b); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x06dafdfd); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf52df05e); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf1fef9b5); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x04640d7f); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 5300000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9ffee); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe6fff3); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00250072); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00af009c); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x000cff10); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe13fdb8); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe870089); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x031104e1); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x04b8020f); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfd98f92f); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf71df8f0); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe8805ce); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7e0c9c); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0808ff44); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf603f086); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf19af922); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x03fb0d5e); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 5400000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffcffef); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe0ffe0); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00050056); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00b000d1); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0071ff82); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe53fd8c); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfddfff99); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x024104a3); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x054a034d); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xff01fa1e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf717f7ed); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfcf50461); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad50cf4); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0921008d); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf6e7f0bd); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf13ff891); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x03920d3b); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 5500000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffffff3); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffd1); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5002f); + cx25840_write4(client, DIF_BPF_COEFF89, 0x009c00ed); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00cb0000); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfebafd94); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd61feb0); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d0422); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x05970464); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0074fb41); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf759f721); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfb7502de); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a000d21); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0a2201d4); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf7d9f104); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf0edf804); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x03280d19); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 5600000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0003fffa); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe3ffc9); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffc90002); + cx25840_write4(client, DIF_BPF_COEFF89, 0x007500ef); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x010e007e); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xff3dfdcf); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd16fddd); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x00440365); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x059b0548); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x01e3fc90); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7dff691); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfa0f014d); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x09020d23); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0b0a0318); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf8d7f15a); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf0a5f779); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x02bd0cf6); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 5700000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00060001); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffecffc9); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffb4ffd4); + cx25840_write4(client, DIF_BPF_COEFF89, 0x004000d5); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x013600f0); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xffd3fe39); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd04fd31); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xff360277); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x055605ef); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x033efdfe); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf8a5f642); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf8cbffb6); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e10cfb); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0bd50456); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf9dff1be); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf067f6f2); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x02520cd2); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 5800000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00080009); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfff8ffd2); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffaaffac); + cx25840_write4(client, DIF_BPF_COEFF89, 0x000200a3); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x013c014a); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x006dfec9); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd2bfcb7); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe350165); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x04cb0651); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0477ff7e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf9a5f635); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf7b1fe20); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x069f0ca8); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0c81058b); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xfaf0f231); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf033f66d); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x01e60cae); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 5900000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0009000e); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0005ffe1); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffacff90); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffc5005f); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x01210184); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x00fcff72); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd8afc77); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51003f); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x04020669); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x05830103); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfad7f66b); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf6c8fc93); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x05430c2b); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d0d06b5); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xfc08f2b2); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf00af5ec); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x017b0c89); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 6000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00070012); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0012fff5); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffbaff82); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff8e000f); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00e80198); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01750028); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe18fc75); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99ff15); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x03050636); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0656027f); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfc32f6e2); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf614fb17); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d20b87); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d7707d2); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xfd26f341); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xefeaf56f); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x010f0c64); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 6100000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00050012); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001c000b); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffd1ff84); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff66ffbe); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00960184); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01cd00da); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfeccfcb2); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fdf9); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x01e005bc); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x06e703e4); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfdabf798); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf599f9b3); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x02510abd); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0dbf08df); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xfe48f3dc); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xefd5f4f6); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x00a20c3e); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 6200000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0002000f); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0021001f); + cx25840_write4(client, DIF_BPF_COEFF67, 0xfff0ff97); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff50ff74); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0034014a); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01fa0179); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xff97fd2a); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fcfa); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x00a304fe); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x07310525); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xff37f886); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf55cf86e); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c709d0); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0de209db); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xff6df484); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xefcbf481); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x00360c18); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 6300000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffe000a); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0021002f); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0010ffb8); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff50ff3b); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xffcc00f0); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01fa01fa); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0069fdd4); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fc26); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xff5d0407); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x07310638); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x00c9f9a8); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf55cf74e); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xff3908c3); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0de20ac3); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0093f537); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xefcbf410); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xffca0bf2); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 6400000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffb0003); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001c0037); + cx25840_write4(client, DIF_BPF_COEFF67, 0x002fffe2); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff66ff17); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff6a007e); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01cd0251); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0134fea5); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fb8b); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe2002e0); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x06e70713); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x0255faf5); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf599f658); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaf0799); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0dbf0b96); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x01b8f5f5); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xefd5f3a3); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xff5e0bca); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 6500000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fffb); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00120037); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00460010); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff8eff0f); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff180000); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01750276); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x01e8ff8d); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fb31); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfcfb0198); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x065607ad); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x03cefc64); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf614f592); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2e0656); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d770c52); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x02daf6bd); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xefeaf33b); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xfef10ba3); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 6600000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff7fff5); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0005002f); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0054003c); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffc5ff22); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfedfff82); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x00fc0267); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0276007e); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fb1c); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfbfe003e); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x05830802); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x0529fdec); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf6c8f4fe); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabd04ff); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d0d0cf6); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x03f8f78f); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf00af2d7); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xfe850b7b); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 6700000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff0); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfff80020); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00560060); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0002ff4e); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec4ff10); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x006d0225); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02d50166); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fb4e); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb35fee1); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0477080e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x065bff82); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf7b1f4a0); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf9610397); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0c810d80); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0510f869); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf033f278); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xfe1a0b52); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 6800000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffaffee); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffec000c); + cx25840_write4(client, DIF_BPF_COEFF67, 0x004c0078); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0040ff8e); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfecafeb6); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xffd301b6); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02fc0235); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fbc5); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfaaafd90); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x033e07d2); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x075b011b); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf8cbf47a); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81f0224); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0bd50def); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0621f94b); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf067f21e); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xfdae0b29); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 6900000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffdffef); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe3fff6); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0037007f); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0075ffdc); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfef2fe7c); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xff3d0122); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02ea02dd); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fc79); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa65fc5d); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x01e3074e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x082102ad); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfa0ff48c); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fe00a9); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0b0a0e43); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0729fa33); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf0a5f1c9); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xfd430b00); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 7000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0001fff3); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffe2); + cx25840_write4(client, DIF_BPF_COEFF67, 0x001b0076); + cx25840_write4(client, DIF_BPF_COEFF89, 0x009c002d); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff35fe68); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfeba0076); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x029f0352); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfd60); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa69fb53); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x00740688); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x08a7042d); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfb75f4d6); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600ff2d); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0a220e7a); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0827fb22); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf0edf17a); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xfcd80ad6); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 7100000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0004fff9); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe0ffd2); + cx25840_write4(client, DIF_BPF_COEFF67, 0xfffb005e); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00b0007a); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff8ffe7c); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe53ffc1); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0221038c); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fe6e); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfab6fa80); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xff010587); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x08e90590); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfcf5f556); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf52bfdb3); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x09210e95); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0919fc15); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf13ff12f); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xfc6e0aab); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 7200000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00070000); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe6ffc9); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffdb0039); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00af00b8); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfff4feb6); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe13ff10); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x01790388); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0311ff92); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb48f9ed); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfd980453); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x08e306cd); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe88f60a); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf482fc40); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x08080e93); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x09fdfd0c); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf19af0ea); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xfc050a81); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 7300000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00080008); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfff0ffc9); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffc1000d); + cx25840_write4(client, DIF_BPF_COEFF89, 0x009800e2); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x005bff10); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe00fe74); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x00b50345); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b000bc); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc18f9a1); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfc4802f9); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x089807dc); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0022f6f0); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf407fada); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x06da0e74); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0ad3fe06); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf1fef0ab); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xfb9c0a55); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 7400000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0008000e); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfffdffd0); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffafffdf); + cx25840_write4(client, DIF_BPF_COEFF89, 0x006e00f2); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00b8ff82); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe1bfdf8); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xffe302c8); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x041301dc); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd1af99e); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfb1e0183); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x080908b5); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x01bcf801); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3bdf985); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x059a0e38); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0b99ff03); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf26cf071); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xfb330a2a); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 7500000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00070011); + cx25840_write4(client, DIF_BPF_COEFF45, 0x000affdf); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffa9ffb5); + cx25840_write4(client, DIF_BPF_COEFF89, 0x003700e6); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x01010000); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe62fda8); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xff140219); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x043502e1); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe42f9e6); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa270000); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x073a0953); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x034cf939); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3a4f845); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x044c0de1); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0c4f0000); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf2e2f03c); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xfacc09fe); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 7600000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00040012); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0016fff3); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffafff95); + cx25840_write4(client, DIF_BPF_COEFF89, 0xfff900c0); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0130007e); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfecefd89); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe560146); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x041303bc); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xff81fa76); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf96cfe7d); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x063209b1); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x04c9fa93); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3bdf71e); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x02f30d6e); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0cf200fd); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf361f00e); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xfa6509d1); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 7700000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00010010); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001e0008); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffc1ff84); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffbc0084); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x013e00f0); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xff56fd9f); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdb8005c); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b00460); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x00c7fb45); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8f4fd07); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x04fa09ce); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x062afc07); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf407f614); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x01920ce0); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0d8301fa); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf3e8efe5); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xfa0009a4); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 7800000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffd000b); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0022001d); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffdbff82); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff870039); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x012a014a); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xffedfde7); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd47ff6b); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x031104c6); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0202fc4c); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8c6fbad); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x039909a7); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0767fd8e); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf482f52b); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x002d0c39); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0e0002f4); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf477efc2); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf99b0977); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 7900000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffa0004); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0020002d); + cx25840_write4(client, DIF_BPF_COEFF67, 0xfffbff91); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff61ffe8); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00f70184); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x0086fe5c); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd0bfe85); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x024104e5); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0323fd7d); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8e2fa79); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x021d093f); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0879ff22); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf52bf465); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfec70b79); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0e6803eb); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf50defa5); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf937094a); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 8000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fffd); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00190036); + cx25840_write4(client, DIF_BPF_COEFF67, 0x001bffaf); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff4fff99); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00aa0198); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x0112fef3); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd09fdb9); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d04be); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x041bfecc); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf947f978); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x00900897); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x095a00b9); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600f3c5); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfd650aa3); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0ebc04de); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf5aaef8e); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf8d5091c); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 8100000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff7fff6); + cx25840_write4(client, DIF_BPF_COEFF45, 0x000e0038); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0037ffd7); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff52ff56); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x004b0184); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x0186ffa1); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd40fd16); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x00440452); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x04de0029); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9f2f8b2); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfefe07b5); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a05024d); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fef34d); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfc0a09b8); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0efa05cd); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf64eef7d); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf87308ed); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 8200000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff0); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00000031); + cx25840_write4(client, DIF_BPF_COEFF67, 0x004c0005); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff6aff27); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xffe4014a); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01d70057); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdacfca6); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xff3603a7); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x05610184); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfadbf82e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfd74069f); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a7503d6); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81ff2ff); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfab808b9); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0f2306b5); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf6f9ef72); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf81308bf); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 8300000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffbffee); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfff30022); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00560032); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff95ff10); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff8000f0); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01fe0106); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe46fc71); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe3502c7); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x059e02ce); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfbf9f7f2); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfbff055b); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0aa9054c); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf961f2db); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf97507aa); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0f350797); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf7a9ef6d); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf7b40890); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 8400000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffeffee); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe8000f); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00540058); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffcdff14); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff29007e); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01f6019e); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xff01fc7c); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd5101bf); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x059203f6); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfd41f7fe); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfaa903f3); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a9e06a9); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabdf2e2); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf842068b); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0f320871); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf85eef6e); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf7560860); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 8500000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0002fff2); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe1fff9); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00460073); + cx25840_write4(client, DIF_BPF_COEFF89, 0x000bff34); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfee90000); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01c10215); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xffd0fcc5); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99009d); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x053d04f1); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfea5f853); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf97d0270); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a5607e4); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2ef314); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf723055f); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0f180943); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf919ef75); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf6fa0830); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 8600000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0005fff8); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffe4); + cx25840_write4(client, DIF_BPF_COEFF67, 0x002f007f); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0048ff6b); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec7ff82); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x0163025f); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x00a2fd47); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17ff73); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x04a405b2); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0017f8ed); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf88500dc); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x09d208f9); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaff370); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf61c0429); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0ee80a0b); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf9d8ef82); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf6a00800); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 8700000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0007ffff); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe1ffd4); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0010007a); + cx25840_write4(client, DIF_BPF_COEFF89, 0x007cffb2); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec6ff10); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x00e60277); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0168fdf9); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fe50); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x03ce0631); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0188f9c8); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7c7ff43); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x091509e3); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xff39f3f6); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf52d02ea); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0ea30ac9); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfa9bef95); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf64607d0); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 8800000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00090007); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe9ffca); + cx25840_write4(client, DIF_BPF_COEFF67, 0xfff00065); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00a10003); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfee6feb6); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x0053025b); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0213fed0); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fd46); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x02c70668); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x02eafadb); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf74bfdae); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x08230a9c); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c7f4a3); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf45b01a6); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0e480b7c); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfb61efae); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf5ef079f); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 8900000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0008000d); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfff5ffc8); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffd10043); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00b20053); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff24fe7c); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xffb9020c); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0295ffbb); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fc64); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x019b0654); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x042dfc1c); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf714fc2a); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x07020b21); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0251f575); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3a7005e); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0dd80c24); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfc2aefcd); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf599076e); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 9000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00060011); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0002ffcf); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffba0018); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00ad009a); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff79fe68); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xff260192); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02e500ab); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fbb6); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x005b05f7); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0545fd81); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf723fabf); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x05b80b70); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d2f669); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf313ff15); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0d550cbf); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfcf6eff2); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf544073d); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 9100000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00030012); + cx25840_write4(client, DIF_BPF_COEFF45, 0x000fffdd); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffacffea); + cx25840_write4(client, DIF_BPF_COEFF89, 0x009300cf); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xffdcfe7c); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfea600f7); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02fd0190); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fb46); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xff150554); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0627fefd); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf778f978); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x044d0b87); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0543f77d); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2a0fdcf); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0cbe0d4e); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfdc4f01d); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4f2070b); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 9200000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00000010); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001afff0); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffaaffbf); + cx25840_write4(client, DIF_BPF_COEFF89, 0x006700ed); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0043feb6); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe460047); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02db0258); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fb1b); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfddc0473); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x06c90082); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf811f85e); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x02c90b66); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x069ff8ad); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf250fc8d); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0c140dcf); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfe93f04d); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4a106d9); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 9300000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffc000c); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00200006); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffb4ff9c); + cx25840_write4(client, DIF_BPF_COEFF89, 0x002f00ef); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00a4ff10); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe0dff92); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x028102f7); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fb37); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfcbf035e); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x07260202); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf8e8f778); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x01340b0d); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e1f9f4); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf223fb51); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0b590e42); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xff64f083); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf45206a7); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 9400000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff90005); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0022001a); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9ff86); + cx25840_write4(client, DIF_BPF_COEFF89, 0xfff000d7); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00f2ff82); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe01fee5); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x01f60362); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fb99); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfbcc0222); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x07380370); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf9f7f6cc); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xff990a7e); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0902fb50); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf21afa1f); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0a8d0ea6); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0034f0bf); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4050675); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 9500000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fffe); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001e002b); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5ff81); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffb400a5); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x01280000); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe24fe50); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x01460390); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfc3a); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb1000ce); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x070104bf); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb37f65f); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe0009bc); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a00fcbb); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf235f8f8); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x09b20efc); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0105f101); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf3ba0642); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 9600000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0001ffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff7); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00150036); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0005ff8c); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff810061); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x013d007e); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe71fddf); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x007c0380); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fd13); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa94ff70); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x068005e2); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfc9bf633); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfc7308ca); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad5fe30); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf274f7e0); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x08c90f43); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x01d4f147); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf371060f); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 9700000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fff1); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00090038); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0025ffa7); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff5e0012); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x013200f0); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfee3fd9b); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xffaa0331); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0311fe15); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa60fe18); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x05bd06d1); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfe1bf64a); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfafa07ae); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7effab); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2d5f6d7); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x07d30f7a); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x02a3f194); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf32905dc); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 9800000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffcffee); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfffb0032); + cx25840_write4(client, DIF_BPF_COEFF67, 0x003fffcd); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff4effc1); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0106014a); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xff6efd8a); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfedd02aa); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b0ff34); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa74fcd7); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x04bf0781); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xffaaf6a3); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf99e066b); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf90128); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf359f5e1); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x06d20fa2); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0370f1e5); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2e405a8); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 9900000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0xffffffee); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffef0024); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0051fffa); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff54ff77); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00be0184); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x0006fdad); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe2701f3); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413005e); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfad1fbba); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x039007ee); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x013bf73d); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf868050a); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c4302a1); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3fdf4fe); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x05c70fba); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x043bf23c); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2a10575); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 10000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0003fff1); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe50011); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00570027); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff70ff3c); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00620198); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x009efe01); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd95011a); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x04350183); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb71fad0); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x023c0812); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x02c3f811); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf75e0390); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c5c0411); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf4c1f432); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x04b30fc1); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0503f297); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2610541); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 10100000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0006fff7); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffdffffc); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00510050); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff9dff18); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfffc0184); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x0128fe80); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd32002e); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x04130292); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc4dfa21); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x00d107ee); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x0435f91c); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf6850205); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c430573); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf5a1f37d); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x03990fba); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x05c7f2f8); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf222050d); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 10200000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0008fffe); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffdfffe7); + cx25840_write4(client, DIF_BPF_COEFF67, 0x003f006e); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffd6ff0f); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff96014a); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x0197ff1f); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd05ff3e); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b0037c); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd59f9b7); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xff5d0781); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x0585fa56); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf5e4006f); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf906c4); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf69df2e0); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x02790fa2); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0688f35d); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf1e604d8); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 10300000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00090005); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe4ffd6); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0025007e); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0014ff20); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff3c00f0); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01e1ffd0); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd12fe5c); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x03110433); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe88f996); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfdf106d1); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x06aafbb7); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf57efed8); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7e07ff); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf7b0f25e); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x01560f7a); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0745f3c7); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf1ac04a4); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 10400000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0008000c); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffedffcb); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0005007d); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0050ff4c); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfef6007e); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01ff0086); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd58fd97); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x024104ad); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xffcaf9c0); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfc9905e2); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x079afd35); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf555fd46); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad50920); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf8d9f1f6); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x00310f43); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x07fdf435); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf174046f); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 10500000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00050011); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfffaffc8); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5006b); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0082ff8c); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfecc0000); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01f00130); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdd2fcfc); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d04e3); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x010efa32); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfb6404bf); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x084efec5); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf569fbc2); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a000a23); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfa15f1ab); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xff0b0efc); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x08b0f4a7); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf13f043a); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 10600000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00020012); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0007ffcd); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9004c); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00a4ffd9); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec3ff82); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01b401c1); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe76fc97); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x004404d2); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0245fae8); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa5f0370); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x08c1005f); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf5bcfa52); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x09020b04); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfb60f17b); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xfde70ea6); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x095df51e); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf10c0405); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 10700000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xffff0011); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0014ffdb); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffb40023); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00b2002a); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfedbff10); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x0150022d); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xff38fc6f); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36047b); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x035efbda); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9940202); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x08ee01f5); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf649f8fe); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e10bc2); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfcb6f169); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xfcc60e42); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0a04f599); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0db03d0); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 10800000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffb000d); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001dffed); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffaafff5); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00aa0077); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff13feb6); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x00ce026b); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x000afc85); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe3503e3); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x044cfcfb); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf90c0082); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x08d5037f); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf710f7cc); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x069f0c59); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfe16f173); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xfbaa0dcf); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0aa5f617); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0ad039b); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 10900000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff90006); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00210003); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffacffc8); + cx25840_write4(client, DIF_BPF_COEFF89, 0x008e00b6); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff63fe7c); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x003a0275); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x00dafcda); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd510313); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0501fe40); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8cbfefd); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x087604f0); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf80af6c2); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x05430cc8); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xff7af19a); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xfa940d4e); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0b3ff699); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0810365); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 11000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0001ffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8ffff); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00210018); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffbaffa3); + cx25840_write4(client, DIF_BPF_COEFF89, 0x006000e1); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xffc4fe68); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xffa0024b); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x019afd66); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc990216); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0575ff99); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8d4fd81); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x07d40640); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf932f5e6); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d20d0d); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x00dff1de); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf9860cbf); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0bd1f71e); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf058032f); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 11100000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff8); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001b0029); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffd1ff8a); + cx25840_write4(client, DIF_BPF_COEFF89, 0x002600f2); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x002cfe7c); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xff0f01f0); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x023bfe20); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc1700fa); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x05a200f7); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf927fc1c); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x06f40765); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfa82f53b); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x02510d27); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0243f23d); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf8810c24); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0c5cf7a7); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf03102fa); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 11200000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffafff2); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00110035); + cx25840_write4(client, DIF_BPF_COEFF67, 0xfff0ff81); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffe700e7); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x008ffeb6); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe94016d); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02b0fefb); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3ffd1); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x05850249); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9c1fadb); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x05de0858); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfbf2f4c4); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c70d17); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x03a0f2b8); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf7870b7c); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0cdff833); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf00d02c4); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 11300000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffdffee); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00040038); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0010ff88); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffac00c2); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00e2ff10); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe3900cb); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f1ffe9); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3feaa); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x05210381); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa9cf9c8); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x04990912); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfd7af484); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xff390cdb); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x04f4f34d); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf69a0ac9); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0d5af8c1); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xefec028e); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 11400000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0000ffee); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfff60033); + cx25840_write4(client, DIF_BPF_COEFF67, 0x002fff9f); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff7b0087); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x011eff82); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe080018); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f900d8); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fd96); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x04790490); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfbadf8ed); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x032f098e); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xff10f47d); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaf0c75); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x063cf3fc); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf5ba0a0b); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0dccf952); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xefcd0258); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 11500000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0004fff1); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffea0026); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0046ffc3); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff5a003c); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x013b0000); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe04ff63); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02c801b8); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fca6); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0397056a); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfcecf853); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x01ad09c9); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x00acf4ad); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2e0be7); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0773f4c2); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf4e90943); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0e35f9e6); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xefb10221); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 11600000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0007fff6); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe20014); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0054ffee); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff4effeb); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0137007e); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe2efebb); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0260027a); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fbe6); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x02870605); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfe4af7fe); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x001d09c1); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0243f515); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabd0b32); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0897f59e); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf4280871); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0e95fa7c); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef9701eb); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 11700000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0008fffd); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffff); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0056001d); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff57ff9c); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x011300f0); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe82fe2e); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x01ca0310); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fb62); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0155065a); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xffbaf7f2); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfe8c0977); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x03cef5b2); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf9610a58); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x09a5f68f); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf3790797); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0eebfb14); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef8001b5); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 11800000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00080004); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe0ffe9); + cx25840_write4(client, DIF_BPF_COEFF67, 0x004c0047); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff75ff58); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00d1014a); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfef9fdc8); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0111036f); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fb21); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x00120665); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x012df82e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfd0708ec); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0542f682); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81f095c); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0a9af792); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf2db06b5); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0f38fbad); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef6c017e); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 11900000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0007000b); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe7ffd8); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00370068); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffa4ff28); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00790184); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xff87fd91); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x00430392); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fb26); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfece0626); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0294f8b2); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb990825); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0698f77f); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fe0842); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0b73f8a7); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf25105cd); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0f7bfc48); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef5a0148); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 12000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00050010); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfff2ffcc); + cx25840_write4(client, DIF_BPF_COEFF67, 0x001b007b); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffdfff10); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00140198); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x0020fd8e); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xff710375); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfb73); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd9a059f); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x03e0f978); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfa4e0726); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x07c8f8a7); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600070c); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0c2ff9c9); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf1db04de); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0fb4fce5); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef4b0111); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 12100000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00010012); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffffffc8); + cx25840_write4(client, DIF_BPF_COEFF67, 0xfffb007e); + cx25840_write4(client, DIF_BPF_COEFF89, 0x001dff14); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xffad0184); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x00b7fdbe); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfea9031b); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fc01); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc8504d6); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0504fa79); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf93005f6); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x08caf9f2); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf52b05c0); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0ccbfaf9); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf17903eb); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0fe3fd83); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef3f00db); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 12200000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffe0011); + cx25840_write4(client, DIF_BPF_COEFF45, 0x000cffcc); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffdb0071); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0058ff32); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff4f014a); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x013cfe1f); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdfb028a); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0311fcc9); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb9d03d6); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x05f4fbad); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf848049d); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0999fb5b); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf4820461); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d46fc32); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf12d02f4); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x1007fe21); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef3600a4); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 12300000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffa000e); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0017ffd9); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffc10055); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0088ff68); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff0400f0); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01a6fea7); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd7501cc); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b0fdc0); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfaef02a8); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x06a7fd07); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf79d0326); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a31fcda); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf40702f3); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d9ffd72); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0f601fa); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x1021fec0); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef2f006d); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 12400000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0001ffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80007); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001fffeb); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffaf002d); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00a8ffb0); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed3007e); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01e9ff4c); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd2000ee); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413fed8); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa82015c); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0715fe7d); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7340198); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a8dfe69); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3bd017c); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0dd5feb8); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0d500fd); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x1031ff60); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef2b0037); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 12500000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff70000); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00220000); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffa90000); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00b30000); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec20000); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x02000000); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd030000); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x04350000); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa5e0000); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x073b0000); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7110000); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0aac0000); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3a40000); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0de70000); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0c90000); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x10360000); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef290000); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 12600000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff9); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001f0015); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffafffd3); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00a80050); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed3ff82); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01e900b4); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd20ff12); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x04130128); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa82fea4); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x07150183); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf734fe68); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a8d0197); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3bdfe84); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0dd50148); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0d5ff03); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x103100a0); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef2bffc9); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 12700000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffafff2); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00170027); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffc1ffab); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00880098); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff04ff10); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01a60159); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd75fe34); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b00240); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfaeffd58); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x06a702f9); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf79dfcda); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a310326); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf407fd0d); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d9f028e); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0f6fe06); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x10210140); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef2fff93); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 12800000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffeffef); + cx25840_write4(client, DIF_BPF_COEFF45, 0x000c0034); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffdbff8f); + cx25840_write4(client, DIF_BPF_COEFF89, 0x005800ce); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff4ffeb6); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x013c01e1); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdfbfd76); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x03110337); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb9dfc2a); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x05f40453); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf848fb63); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x099904a5); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf482fb9f); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d4603ce); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf12dfd0c); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x100701df); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef36ff5c); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 12900000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0001ffee); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffff0038); + cx25840_write4(client, DIF_BPF_COEFF67, 0xfffbff82); + cx25840_write4(client, DIF_BPF_COEFF89, 0x001d00ec); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xffadfe7c); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x00b70242); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfea9fce5); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x024103ff); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc85fb2a); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x05040587); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf930fa0a); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x08ca060e); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf52bfa40); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0ccb0507); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf179fc15); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0fe3027d); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef3fff25); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 13000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0005fff0); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfff20034); + cx25840_write4(client, DIF_BPF_COEFF67, 0x001bff85); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffdf00f0); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0014fe68); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x00200272); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xff71fc8b); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d048d); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd9afa61); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x03e00688); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfa4ef8da); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x07c80759); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600f8f4); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0c2f0637); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf1dbfb22); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0fb4031b); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef4bfeef); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 13100000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0007fff5); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe70028); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0037ff98); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffa400d8); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0079fe7c); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xff87026f); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0043fc6e); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x004404da); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfecef9da); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0294074e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb99f7db); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x06980881); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fef7be); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0b730759); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf251fa33); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0f7b03b8); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef5afeb8); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 13200000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0008fffc); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe00017); + cx25840_write4(client, DIF_BPF_COEFF67, 0x004cffb9); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff7500a8); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00d1feb6); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfef90238); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0111fc91); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xff3604df); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0012f99b); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x012d07d2); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfd07f714); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0542097e); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81ff6a4); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0a9a086e); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf2dbf94b); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0f380453); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef6cfe82); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 13300000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00080003); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffde0001); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0056ffe3); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff570064); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0113ff10); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe8201d2); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x01cafcf0); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35049e); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0155f9a6); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xffba080e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfe8cf689); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x03ce0a4e); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf961f5a8); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x09a50971); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf379f869); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0eeb04ec); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef80fe4b); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 13400000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0007000a); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe2ffec); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00540012); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff4e0015); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0137ff82); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe2e0145); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0260fd86); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51041a); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0287f9fb); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfe4a0802); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x001df63f); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x02430aeb); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabdf4ce); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x08970a62); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf428f78f); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0e950584); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef97fe15); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 13500000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0004000f); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffeaffda); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0046003d); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff5affc4); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x013b0000); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe04009d); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02c8fe48); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99035a); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0397fa96); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfcec07ad); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x01adf637); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x00ac0b53); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2ef419); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x07730b3e); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf4e9f6bd); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0e35061a); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xefb1fddf); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 13600000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00000012); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfff6ffcd); + cx25840_write4(client, DIF_BPF_COEFF67, 0x002f0061); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff7bff79); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x011e007e); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe08ffe8); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f9ff28); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17026a); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0479fb70); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfbad0713); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x032ff672); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xff100b83); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaff38b); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x063c0c04); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf5baf5f5); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0dcc06ae); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xefcdfda8); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 13700000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffd0012); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0004ffc8); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00100078); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffacff3e); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00e200f0); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe39ff35); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f10017); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd30156); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0521fc7f); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa9c0638); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x0499f6ee); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfd7a0b7c); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xff39f325); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x04f40cb3); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf69af537); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0d5a073f); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xefecfd72); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 13800000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0001fffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffa000e); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0011ffcb); + cx25840_write4(client, DIF_BPF_COEFF67, 0xfff0007f); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffe7ff19); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x008f014a); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe94fe93); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02b00105); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3002f); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0585fdb7); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9c10525); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x05def7a8); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfbf20b3c); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c7f2e9); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x03a00d48); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf787f484); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0cdf07cd); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf00dfd3c); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 13900000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80008); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001bffd7); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffd10076); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0026ff0e); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x002c0184); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xff0ffe10); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x023b01e0); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17ff06); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x05a2ff09); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf92703e4); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x06f4f89b); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfa820ac5); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0251f2d9); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x02430dc3); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf881f3dc); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0c5c0859); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf031fd06); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 14000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80001); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0021ffe8); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffba005d); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0060ff1f); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xffc40198); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xffa0fdb5); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x019a029a); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fdea); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x05750067); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8d4027f); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x07d4f9c0); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf9320a1a); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d2f2f3); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x00df0e22); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf986f341); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0bd108e2); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf058fcd1); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 14100000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fffa); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0021fffd); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffac0038); + cx25840_write4(client, DIF_BPF_COEFF89, 0x008eff4a); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff630184); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x003afd8b); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x00da0326); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fced); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x050101c0); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8cb0103); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x0876fb10); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf80a093e); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0543f338); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xff7a0e66); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xfa94f2b2); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0b3f0967); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf081fc9b); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 14200000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffbfff3); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001d0013); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffaa000b); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00aaff89); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff13014a); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x00cefd95); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x000a037b); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fc1d); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x044c0305); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf90cff7e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x08d5fc81); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf7100834); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x069ff3a7); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfe160e8d); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xfbaaf231); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0aa509e9); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0adfc65); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 14300000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0xffffffef); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00140025); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffb4ffdd); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00b2ffd6); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfedb00f0); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x0150fdd3); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xff380391); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fb85); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x035e0426); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf994fdfe); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x08eefe0b); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf6490702); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e1f43e); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfcb60e97); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xfcc6f1be); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0a040a67); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0dbfc30); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 14400000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0002ffee); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00070033); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9ffb4); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00a40027); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec3007e); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01b4fe3f); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe760369); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fb2e); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x02450518); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa5ffc90); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x08c1ffa1); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf5bc05ae); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0902f4fc); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfb600e85); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xfde7f15a); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x095d0ae2); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf10cfbfb); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 14500000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0002); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0005ffef); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfffa0038); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5ff95); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00820074); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfecc0000); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01f0fed0); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdd20304); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfb1d); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x010e05ce); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfb64fb41); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x084e013b); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf569043e); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a00f5dd); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfa150e55); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xff0bf104); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x08b00b59); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf13ffbc6); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 14600000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0008fff4); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffed0035); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0005ff83); + cx25840_write4(client, DIF_BPF_COEFF89, 0x005000b4); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfef6ff82); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01ffff7a); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd580269); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fb53); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xffca0640); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfc99fa1e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x079a02cb); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf55502ba); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad5f6e0); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf8d90e0a); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0031f0bd); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x07fd0bcb); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf174fb91); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 14700000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0009fffb); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe4002a); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0025ff82); + cx25840_write4(client, DIF_BPF_COEFF89, 0x001400e0); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff3cff10); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01e10030); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd1201a4); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0311fbcd); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe88066a); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfdf1f92f); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x06aa0449); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf57e0128); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7ef801); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf7b00da2); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0156f086); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x07450c39); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf1acfb5c); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 14800000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00080002); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffdf0019); + cx25840_write4(client, DIF_BPF_COEFF67, 0x003fff92); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffd600f1); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff96feb6); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x019700e1); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd0500c2); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b0fc84); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd590649); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xff5df87f); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x058505aa); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf5e4ff91); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf9f93c); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf69d0d20); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0279f05e); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x06880ca3); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf1e6fb28); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 14900000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00060009); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffdf0004); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0051ffb0); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff9d00e8); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfffcfe7c); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01280180); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd32ffd2); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413fd6e); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc4d05df); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x00d1f812); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x043506e4); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf685fdfb); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c43fa8d); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf5a10c83); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0399f046); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x05c70d08); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf222faf3); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 15000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0003000f); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe5ffef); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0057ffd9); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff7000c4); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0062fe68); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x009e01ff); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd95fee6); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0435fe7d); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb710530); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x023cf7ee); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x02c307ef); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf75efc70); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c5cfbef); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf4c10bce); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x04b3f03f); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x05030d69); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf261fabf); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 15100000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xffff0012); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffefffdc); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00510006); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff540089); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00befe7c); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x00060253); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe27fe0d); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413ffa2); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfad10446); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0390f812); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x013b08c3); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf868faf6); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c43fd5f); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3fd0b02); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x05c7f046); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x043b0dc4); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2a1fa8b); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 15200000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0001fffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffc0012); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfffbffce); + cx25840_write4(client, DIF_BPF_COEFF67, 0x003f0033); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff4e003f); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0106feb6); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xff6e0276); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfeddfd56); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b000cc); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa740329); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x04bff87f); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xffaa095d); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf99ef995); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf9fed8); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3590a1f); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x06d2f05e); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x03700e1b); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2e4fa58); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 15300000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0001ffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9000f); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0009ffc8); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00250059); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff5effee); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0132ff10); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfee30265); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xffaafccf); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x031101eb); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa6001e8); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x05bdf92f); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfe1b09b6); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfafaf852); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7e0055); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2d50929); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x07d3f086); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x02a30e6c); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf329fa24); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 15400000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80009); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0015ffca); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00050074); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff81ff9f); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x013dff82); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe710221); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x007cfc80); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x024102ed); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa940090); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0680fa1e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfc9b09cd); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfc73f736); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad501d0); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2740820); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x08c9f0bd); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x01d40eb9); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf371f9f1); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 15500000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80002); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001effd5); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5007f); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffb4ff5b); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x01280000); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe2401b0); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0146fc70); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d03c6); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb10ff32); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0701fb41); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb3709a1); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe00f644); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a000345); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2350708); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x09b2f104); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x01050eff); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf3baf9be); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 15600000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fffb); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0022ffe6); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9007a); + cx25840_write4(client, DIF_BPF_COEFF89, 0xfff0ff29); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00f2007e); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe01011b); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x01f6fc9e); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x00440467); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfbccfdde); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0738fc90); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf9f70934); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xff99f582); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x090204b0); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf21a05e1); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0a8df15a); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x00340f41); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf405f98b); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 15700000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffcfff4); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0020fffa); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffb40064); + cx25840_write4(client, DIF_BPF_COEFF89, 0x002fff11); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00a400f0); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe0d006e); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0281fd09); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xff3604c9); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfcbffca2); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0726fdfe); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf8e80888); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0134f4f3); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e1060c); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf22304af); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0b59f1be); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xff640f7d); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf452f959); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 15800000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0000fff0); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001a0010); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffaa0041); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0067ff13); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0043014a); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe46ffb9); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02dbfda8); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe3504e5); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfddcfb8d); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x06c9ff7e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf81107a2); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x02c9f49a); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x069f0753); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2500373); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0c14f231); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfe930fb3); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4a1f927); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 15900000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0002); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0003ffee); + cx25840_write4(client, DIF_BPF_COEFF45, 0x000f0023); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffac0016); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0093ff31); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xffdc0184); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfea6ff09); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02fdfe70); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd5104ba); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xff15faac); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x06270103); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7780688); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x044df479); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x05430883); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2a00231); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0cbef2b2); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfdc40fe3); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4f2f8f5); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 16000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0006ffef); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00020031); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffbaffe8); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00adff66); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff790198); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xff26fe6e); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02e5ff55); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99044a); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x005bfa09); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0545027f); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7230541); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x05b8f490); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d20997); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf31300eb); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0d55f341); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfcf6100e); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf544f8c3); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + } +} + +static void cx23888_std_setup(struct i2c_client *client) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + v4l2_std_id std = state->std; + u32 ifHz; + + cx25840_write4(client, 0x478, 0x6628021F); + cx25840_write4(client, 0x400, 0x0); + cx25840_write4(client, 0x4b4, 0x20524030); + cx25840_write4(client, 0x47c, 0x010a8263); + + if (std & V4L2_STD_NTSC) { + v4l_dbg(1, cx25840_debug, client, "%s() Selecting NTSC", + __func__); + + /* Horiz / vert timing */ + cx25840_write4(client, 0x428, 0x1e1e601a); + cx25840_write4(client, 0x424, 0x5b2d007a); + + /* DIF NTSC */ + cx25840_write4(client, 0x304, 0x6503bc0c); + cx25840_write4(client, 0x308, 0xbd038c85); + cx25840_write4(client, 0x30c, 0x1db4640a); + cx25840_write4(client, 0x310, 0x00008800); + cx25840_write4(client, 0x314, 0x44400400); + cx25840_write4(client, 0x32c, 0x0c800800); + cx25840_write4(client, 0x330, 0x27000100); + cx25840_write4(client, 0x334, 0x1f296e1f); + cx25840_write4(client, 0x338, 0x009f50c1); + cx25840_write4(client, 0x340, 0x1befbf06); + cx25840_write4(client, 0x344, 0x000035e8); + + /* DIF I/F */ + ifHz = 5400000; + + } else { + v4l_dbg(1, cx25840_debug, client, "%s() Selecting PAL-BG", + __func__); + + /* Horiz / vert timing */ + cx25840_write4(client, 0x428, 0x28244024); + cx25840_write4(client, 0x424, 0x5d2d0084); + + /* DIF */ + cx25840_write4(client, 0x304, 0x6503bc0c); + cx25840_write4(client, 0x308, 0xbd038c85); + cx25840_write4(client, 0x30c, 0x1db4640a); + cx25840_write4(client, 0x310, 0x00008800); + cx25840_write4(client, 0x314, 0x44400600); + cx25840_write4(client, 0x32c, 0x0c800800); + cx25840_write4(client, 0x330, 0x27000100); + cx25840_write4(client, 0x334, 0x213530ec); + cx25840_write4(client, 0x338, 0x00a65ba8); + cx25840_write4(client, 0x340, 0x1befbf06); + cx25840_write4(client, 0x344, 0x000035e8); + + /* DIF I/F */ + ifHz = 6000000; + } + + cx23885_dif_setup(client, ifHz); + + /* Explicitly ensure the inputs are reconfigured after + * a standard change. + */ + set_input(client, state->vid_input, state->aud_input); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops cx25840_ctrl_ops = { + .s_ctrl = cx25840_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops cx25840_core_ops = { + .log_status = cx25840_log_status, + .g_chip_ident = cx25840_g_chip_ident, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, + .s_std = cx25840_s_std, + .g_std = cx25840_g_std, + .reset = cx25840_reset, + .load_fw = cx25840_load_fw, + .s_io_pin_config = common_s_io_pin_config, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = cx25840_g_register, + .s_register = cx25840_s_register, +#endif + .interrupt_service_routine = cx25840_irq_handler, +}; + +static const struct v4l2_subdev_tuner_ops cx25840_tuner_ops = { + .s_frequency = cx25840_s_frequency, + .s_radio = cx25840_s_radio, + .g_tuner = cx25840_g_tuner, + .s_tuner = cx25840_s_tuner, +}; + +static const struct v4l2_subdev_audio_ops cx25840_audio_ops = { + .s_clock_freq = cx25840_s_clock_freq, + .s_routing = cx25840_s_audio_routing, + .s_stream = cx25840_s_audio_stream, +}; + +static const struct v4l2_subdev_video_ops cx25840_video_ops = { + .s_routing = cx25840_s_video_routing, + .s_mbus_fmt = cx25840_s_mbus_fmt, + .s_stream = cx25840_s_stream, + .g_input_status = cx25840_g_input_status, +}; + +static const struct v4l2_subdev_vbi_ops cx25840_vbi_ops = { + .decode_vbi_line = cx25840_decode_vbi_line, + .s_raw_fmt = cx25840_s_raw_fmt, + .s_sliced_fmt = cx25840_s_sliced_fmt, + .g_sliced_fmt = cx25840_g_sliced_fmt, +}; + +static const struct v4l2_subdev_ops cx25840_ops = { + .core = &cx25840_core_ops, + .tuner = &cx25840_tuner_ops, + .audio = &cx25840_audio_ops, + .video = &cx25840_video_ops, + .vbi = &cx25840_vbi_ops, + .ir = &cx25840_ir_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static u32 get_cx2388x_ident(struct i2c_client *client) +{ + u32 ret; + + /* Come out of digital power down */ + cx25840_write(client, 0x000, 0); + + /* Detecting whether the part is cx23885/7/8 is more + * difficult than it needs to be. No ID register. Instead we + * probe certain registers indicated in the datasheets to look + * for specific defaults that differ between the silicon designs. */ + + /* It's either 885/7 if the IR Tx Clk Divider register exists */ + if (cx25840_read4(client, 0x204) & 0xffff) { + /* CX23885 returns bogus repetitive byte values for the DIF, + * which doesn't exist for it. (Ex. 8a8a8a8a or 31313131) */ + ret = cx25840_read4(client, 0x300); + if (((ret & 0xffff0000) >> 16) == (ret & 0xffff)) { + /* No DIF */ + ret = V4L2_IDENT_CX23885_AV; + } else { + /* CX23887 has a broken DIF, but the registers + * appear valid (but unused), good enough to detect. */ + ret = V4L2_IDENT_CX23887_AV; + } + } else if (cx25840_read4(client, 0x300) & 0x0fffffff) { + /* DIF PLL Freq Word reg exists; chip must be a CX23888 */ + ret = V4L2_IDENT_CX23888_AV; + } else { + v4l_err(client, "Unable to detect h/w, assuming cx23887\n"); + ret = V4L2_IDENT_CX23887_AV; + } + + /* Back into digital power down */ + cx25840_write(client, 0x000, 2); + return ret; +} + +static int cx25840_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct cx25840_state *state; + struct v4l2_subdev *sd; + int default_volume; + u32 id = V4L2_IDENT_NONE; + u16 device_id; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_dbg(1, cx25840_debug, client, "detecting cx25840 client on address 0x%x\n", client->addr << 1); + + device_id = cx25840_read(client, 0x101) << 8; + device_id |= cx25840_read(client, 0x100); + v4l_dbg(1, cx25840_debug, client, "device_id = 0x%04x\n", device_id); + + /* The high byte of the device ID should be + * 0x83 for the cx2583x and 0x84 for the cx2584x */ + if ((device_id & 0xff00) == 0x8300) { + id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6; + } else if ((device_id & 0xff00) == 0x8400) { + id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf); + } else if (device_id == 0x0000) { + id = get_cx2388x_ident(client); + } else if ((device_id & 0xfff0) == 0x5A30) { + /* The CX23100 (0x5A3C = 23100) doesn't have an A/V decoder */ + id = V4L2_IDENT_CX2310X_AV; + } else if ((device_id & 0xff) == (device_id >> 8)) { + v4l_err(client, + "likely a confused/unresponsive cx2388[578] A/V decoder" + " found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + v4l_err(client, "A method to reset it from the cx25840 driver" + " software is not known at this time\n"); + return -ENODEV; + } else { + v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n"); + return -ENODEV; + } + + state = kzalloc(sizeof(struct cx25840_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &cx25840_ops); + + switch (id) { + case V4L2_IDENT_CX23885_AV: + v4l_info(client, "cx23885 A/V decoder found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + break; + case V4L2_IDENT_CX23887_AV: + v4l_info(client, "cx23887 A/V decoder found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + break; + case V4L2_IDENT_CX23888_AV: + v4l_info(client, "cx23888 A/V decoder found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + break; + case V4L2_IDENT_CX2310X_AV: + v4l_info(client, "cx%d A/V decoder found @ 0x%x (%s)\n", + device_id, client->addr << 1, client->adapter->name); + break; + case V4L2_IDENT_CX25840: + case V4L2_IDENT_CX25841: + case V4L2_IDENT_CX25842: + case V4L2_IDENT_CX25843: + /* Note: revision '(device_id & 0x0f) == 2' was never built. The + marking skips from 0x1 == 22 to 0x3 == 23. */ + v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n", + (device_id & 0xfff0) >> 4, + (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 + : (device_id & 0x0f), + client->addr << 1, client->adapter->name); + break; + case V4L2_IDENT_CX25836: + case V4L2_IDENT_CX25837: + default: + v4l_info(client, "cx25%3x-%x found @ 0x%x (%s)\n", + (device_id & 0xfff0) >> 4, device_id & 0x0f, + client->addr << 1, client->adapter->name); + break; + } + + state->c = client; + state->vid_input = CX25840_COMPOSITE7; + state->aud_input = CX25840_AUDIO8; + state->audclk_freq = 48000; + state->audmode = V4L2_TUNER_MODE_LANG1; + state->vbi_line_offset = 8; + state->id = id; + state->rev = device_id; + v4l2_ctrl_handler_init(&state->hdl, 9); + v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, + V4L2_CID_CONTRAST, 0, 127, 1, 64); + v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, + V4L2_CID_SATURATION, 0, 127, 1, 64); + v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + if (!is_cx2583x(state)) { + default_volume = cx25840_read(client, 0x8d4); + /* + * Enforce the legacy PVR-350/MSP3400 to PVR-150/CX25843 volume + * scale mapping limits to avoid -ERANGE errors when + * initializing the volume control + */ + if (default_volume > 228) { + /* Bottom out at -96 dB, v4l2 vol range 0x2e00-0x2fff */ + default_volume = 228; + cx25840_write(client, 0x8d4, 228); + } + else if (default_volume < 20) { + /* Top out at + 8 dB, v4l2 vol range 0xfe00-0xffff */ + default_volume = 20; + cx25840_write(client, 0x8d4, 20); + } + default_volume = (((228 - default_volume) >> 1) + 23) << 9; + + state->volume = v4l2_ctrl_new_std(&state->hdl, + &cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_VOLUME, + 0, 65535, 65535 / 100, default_volume); + state->mute = v4l2_ctrl_new_std(&state->hdl, + &cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_MUTE, + 0, 1, 1, 0); + v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops, + V4L2_CID_AUDIO_BALANCE, + 0, 65535, 65535 / 100, 32768); + v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops, + V4L2_CID_AUDIO_BASS, + 0, 65535, 65535 / 100, 32768); + v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops, + V4L2_CID_AUDIO_TREBLE, + 0, 65535, 65535 / 100, 32768); + } + sd->ctrl_handler = &state->hdl; + if (state->hdl.error) { + int err = state->hdl.error; + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + if (!is_cx2583x(state)) + v4l2_ctrl_cluster(2, &state->volume); + v4l2_ctrl_handler_setup(&state->hdl); + + if (client->dev.platform_data) { + struct cx25840_platform_data *pdata = client->dev.platform_data; + + state->pvr150_workaround = pdata->pvr150_workaround; + } + + cx25840_ir_probe(sd); + return 0; +} + +static int cx25840_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(sd); + + cx25840_ir_remove(sd); + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return 0; +} + +static const struct i2c_device_id cx25840_id[] = { + { "cx25840", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cx25840_id); + +static struct i2c_driver cx25840_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "cx25840", + }, + .probe = cx25840_probe, + .remove = cx25840_remove, + .id_table = cx25840_id, +}; + +module_i2c_driver(cx25840_driver); diff --git a/drivers/media/i2c/cx25840/cx25840-core.h b/drivers/media/i2c/cx25840/cx25840-core.h new file mode 100644 index 00000000000..bd4ada28b49 --- /dev/null +++ b/drivers/media/i2c/cx25840/cx25840-core.h @@ -0,0 +1,137 @@ +/* cx25840 internal API header + * + * Copyright (C) 2003-2004 Chris Kennedy + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _CX25840_CORE_H_ +#define _CX25840_CORE_H_ + + +#include +#include +#include +#include +#include + +struct cx25840_ir_state; + +struct cx25840_state { + struct i2c_client *c; + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + struct { + /* volume cluster */ + struct v4l2_ctrl *volume; + struct v4l2_ctrl *mute; + }; + int pvr150_workaround; + int radio; + v4l2_std_id std; + enum cx25840_video_input vid_input; + enum cx25840_audio_input aud_input; + u32 audclk_freq; + int audmode; + int vbi_line_offset; + u32 id; + u32 rev; + int is_initialized; + wait_queue_head_t fw_wait; /* wake up when the fw load is finished */ + struct work_struct fw_work; /* work entry for fw load */ + struct cx25840_ir_state *ir_state; +}; + +static inline struct cx25840_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct cx25840_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct cx25840_state, hdl)->sd; +} + +static inline bool is_cx2583x(struct cx25840_state *state) +{ + return state->id == V4L2_IDENT_CX25836 || + state->id == V4L2_IDENT_CX25837; +} + +static inline bool is_cx231xx(struct cx25840_state *state) +{ + return state->id == V4L2_IDENT_CX2310X_AV; +} + +static inline bool is_cx2388x(struct cx25840_state *state) +{ + return state->id == V4L2_IDENT_CX23885_AV || + state->id == V4L2_IDENT_CX23887_AV || + state->id == V4L2_IDENT_CX23888_AV; +} + +static inline bool is_cx23885(struct cx25840_state *state) +{ + return state->id == V4L2_IDENT_CX23885_AV; +} + +static inline bool is_cx23887(struct cx25840_state *state) +{ + return state->id == V4L2_IDENT_CX23887_AV; +} + +static inline bool is_cx23888(struct cx25840_state *state) +{ + return state->id == V4L2_IDENT_CX23888_AV; +} + +/* ----------------------------------------------------------------------- */ +/* cx25850-core.c */ +int cx25840_write(struct i2c_client *client, u16 addr, u8 value); +int cx25840_write4(struct i2c_client *client, u16 addr, u32 value); +u8 cx25840_read(struct i2c_client *client, u16 addr); +u32 cx25840_read4(struct i2c_client *client, u16 addr); +int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned mask, u8 value); +int cx25840_and_or4(struct i2c_client *client, u16 addr, u32 and_mask, + u32 or_value); +void cx25840_std_setup(struct i2c_client *client); + +/* ----------------------------------------------------------------------- */ +/* cx25850-firmware.c */ +int cx25840_loadfw(struct i2c_client *client); + +/* ----------------------------------------------------------------------- */ +/* cx25850-audio.c */ +void cx25840_audio_set_path(struct i2c_client *client); +int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq); + +extern const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops; + +/* ----------------------------------------------------------------------- */ +/* cx25850-vbi.c */ +int cx25840_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt); +int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt); +int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt); +int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi); + +/* ----------------------------------------------------------------------- */ +/* cx25850-ir.c */ +extern const struct v4l2_subdev_ir_ops cx25840_ir_ops; +int cx25840_ir_log_status(struct v4l2_subdev *sd); +int cx25840_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled); +int cx25840_ir_probe(struct v4l2_subdev *sd); +int cx25840_ir_remove(struct v4l2_subdev *sd); + +#endif diff --git a/drivers/media/i2c/cx25840/cx25840-firmware.c b/drivers/media/i2c/cx25840/cx25840-firmware.c new file mode 100644 index 00000000000..b3169f94ece --- /dev/null +++ b/drivers/media/i2c/cx25840/cx25840-firmware.c @@ -0,0 +1,175 @@ +/* cx25840 firmware functions + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include + +#include "cx25840-core.h" + +/* + * Mike Isely - The FWSEND parameter controls the + * size of the firmware chunks sent down the I2C bus to the chip. + * Previously this had been set to 1024 but unfortunately some I2C + * implementations can't transfer data in such big gulps. + * Specifically, the pvrusb2 driver has a hard limit of around 60 + * bytes, due to the encapsulation there of I2C traffic into USB + * messages. So we have to significantly reduce this parameter. + */ +#define FWSEND 48 + +#define FWDEV(x) &((x)->dev) + +static char *firmware = ""; + +module_param(firmware, charp, 0444); + +MODULE_PARM_DESC(firmware, "Firmware image to load"); + +static void start_fw_load(struct i2c_client *client) +{ + /* DL_ADDR_LB=0 DL_ADDR_HB=0 */ + cx25840_write(client, 0x800, 0x00); + cx25840_write(client, 0x801, 0x00); + // DL_MAP=3 DL_AUTO_INC=0 DL_ENABLE=1 + cx25840_write(client, 0x803, 0x0b); + /* AUTO_INC_DIS=1 */ + cx25840_write(client, 0x000, 0x20); +} + +static void end_fw_load(struct i2c_client *client) +{ + /* AUTO_INC_DIS=0 */ + cx25840_write(client, 0x000, 0x00); + /* DL_ENABLE=0 */ + cx25840_write(client, 0x803, 0x03); +} + +#define CX2388x_FIRMWARE "v4l-cx23885-avcore-01.fw" +#define CX231xx_FIRMWARE "v4l-cx231xx-avcore-01.fw" +#define CX25840_FIRMWARE "v4l-cx25840.fw" + +static const char *get_fw_name(struct i2c_client *client) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + + if (firmware[0]) + return firmware; + if (is_cx2388x(state)) + return CX2388x_FIRMWARE; + if (is_cx231xx(state)) + return CX231xx_FIRMWARE; + return CX25840_FIRMWARE; +} + +static int check_fw_load(struct i2c_client *client, int size) +{ + /* DL_ADDR_HB DL_ADDR_LB */ + int s = cx25840_read(client, 0x801) << 8; + s |= cx25840_read(client, 0x800); + + if (size != s) { + v4l_err(client, "firmware %s load failed\n", + get_fw_name(client)); + return -EINVAL; + } + + v4l_info(client, "loaded %s firmware (%d bytes)\n", + get_fw_name(client), size); + return 0; +} + +static int fw_write(struct i2c_client *client, const u8 *data, int size) +{ + if (i2c_master_send(client, data, size) < size) { + v4l_err(client, "firmware load i2c failure\n"); + return -ENOSYS; + } + + return 0; +} + +int cx25840_loadfw(struct i2c_client *client) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + const struct firmware *fw = NULL; + u8 buffer[FWSEND]; + const u8 *ptr; + const char *fwname = get_fw_name(client); + int size, retval; + int MAX_BUF_SIZE = FWSEND; + u32 gpio_oe = 0, gpio_da = 0; + + if (is_cx2388x(state)) { + /* Preserve the GPIO OE and output bits */ + gpio_oe = cx25840_read(client, 0x160); + gpio_da = cx25840_read(client, 0x164); + } + + if (is_cx231xx(state) && MAX_BUF_SIZE > 16) { + v4l_err(client, " Firmware download size changed to 16 bytes max length\n"); + MAX_BUF_SIZE = 16; /* cx231xx cannot accept more than 16 bytes at a time */ + } + + if (request_firmware(&fw, fwname, FWDEV(client)) != 0) { + v4l_err(client, "unable to open firmware %s\n", fwname); + return -EINVAL; + } + + start_fw_load(client); + + buffer[0] = 0x08; + buffer[1] = 0x02; + + size = fw->size; + ptr = fw->data; + while (size > 0) { + int len = min(MAX_BUF_SIZE - 2, size); + + memcpy(buffer + 2, ptr, len); + + retval = fw_write(client, buffer, len + 2); + + if (retval < 0) { + release_firmware(fw); + return retval; + } + + size -= len; + ptr += len; + } + + end_fw_load(client); + + size = fw->size; + release_firmware(fw); + + if (is_cx2388x(state)) { + /* Restore GPIO configuration after f/w load */ + cx25840_write(client, 0x160, gpio_oe); + cx25840_write(client, 0x164, gpio_da); + } + + return check_fw_load(client, size); +} + +MODULE_FIRMWARE(CX2388x_FIRMWARE); +MODULE_FIRMWARE(CX231xx_FIRMWARE); +MODULE_FIRMWARE(CX25840_FIRMWARE); + diff --git a/drivers/media/i2c/cx25840/cx25840-ir.c b/drivers/media/i2c/cx25840/cx25840-ir.c new file mode 100644 index 00000000000..38ce76ed192 --- /dev/null +++ b/drivers/media/i2c/cx25840/cx25840-ir.c @@ -0,0 +1,1281 @@ +/* + * Driver for the Conexant CX2584x Audio/Video decoder chip and related cores + * + * Integrated Consumer Infrared Controller + * + * Copyright (C) 2010 Andy Walls + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include +#include +#include + +#include "cx25840-core.h" + +static unsigned int ir_debug; +module_param(ir_debug, int, 0644); +MODULE_PARM_DESC(ir_debug, "enable integrated IR debug messages"); + +#define CX25840_IR_REG_BASE 0x200 + +#define CX25840_IR_CNTRL_REG 0x200 +#define CNTRL_WIN_3_3 0x00000000 +#define CNTRL_WIN_4_3 0x00000001 +#define CNTRL_WIN_3_4 0x00000002 +#define CNTRL_WIN_4_4 0x00000003 +#define CNTRL_WIN 0x00000003 +#define CNTRL_EDG_NONE 0x00000000 +#define CNTRL_EDG_FALL 0x00000004 +#define CNTRL_EDG_RISE 0x00000008 +#define CNTRL_EDG_BOTH 0x0000000C +#define CNTRL_EDG 0x0000000C +#define CNTRL_DMD 0x00000010 +#define CNTRL_MOD 0x00000020 +#define CNTRL_RFE 0x00000040 +#define CNTRL_TFE 0x00000080 +#define CNTRL_RXE 0x00000100 +#define CNTRL_TXE 0x00000200 +#define CNTRL_RIC 0x00000400 +#define CNTRL_TIC 0x00000800 +#define CNTRL_CPL 0x00001000 +#define CNTRL_LBM 0x00002000 +#define CNTRL_R 0x00004000 + +#define CX25840_IR_TXCLK_REG 0x204 +#define TXCLK_TCD 0x0000FFFF + +#define CX25840_IR_RXCLK_REG 0x208 +#define RXCLK_RCD 0x0000FFFF + +#define CX25840_IR_CDUTY_REG 0x20C +#define CDUTY_CDC 0x0000000F + +#define CX25840_IR_STATS_REG 0x210 +#define STATS_RTO 0x00000001 +#define STATS_ROR 0x00000002 +#define STATS_RBY 0x00000004 +#define STATS_TBY 0x00000008 +#define STATS_RSR 0x00000010 +#define STATS_TSR 0x00000020 + +#define CX25840_IR_IRQEN_REG 0x214 +#define IRQEN_RTE 0x00000001 +#define IRQEN_ROE 0x00000002 +#define IRQEN_RSE 0x00000010 +#define IRQEN_TSE 0x00000020 +#define IRQEN_MSK 0x00000033 + +#define CX25840_IR_FILTR_REG 0x218 +#define FILTR_LPF 0x0000FFFF + +#define CX25840_IR_FIFO_REG 0x23C +#define FIFO_RXTX 0x0000FFFF +#define FIFO_RXTX_LVL 0x00010000 +#define FIFO_RXTX_RTO 0x0001FFFF +#define FIFO_RX_NDV 0x00020000 +#define FIFO_RX_DEPTH 8 +#define FIFO_TX_DEPTH 8 + +#define CX25840_VIDCLK_FREQ 108000000 /* 108 MHz, BT.656 */ +#define CX25840_IR_REFCLK_FREQ (CX25840_VIDCLK_FREQ / 2) + +/* + * We use this union internally for convenience, but callers to tx_write + * and rx_read will be expecting records of type struct ir_raw_event. + * Always ensure the size of this union is dictated by struct ir_raw_event. + */ +union cx25840_ir_fifo_rec { + u32 hw_fifo_data; + struct ir_raw_event ir_core_data; +}; + +#define CX25840_IR_RX_KFIFO_SIZE (256 * sizeof(union cx25840_ir_fifo_rec)) +#define CX25840_IR_TX_KFIFO_SIZE (256 * sizeof(union cx25840_ir_fifo_rec)) + +struct cx25840_ir_state { + struct i2c_client *c; + + struct v4l2_subdev_ir_parameters rx_params; + struct mutex rx_params_lock; /* protects Rx parameter settings cache */ + atomic_t rxclk_divider; + atomic_t rx_invert; + + struct kfifo rx_kfifo; + spinlock_t rx_kfifo_lock; /* protect Rx data kfifo */ + + struct v4l2_subdev_ir_parameters tx_params; + struct mutex tx_params_lock; /* protects Tx parameter settings cache */ + atomic_t txclk_divider; +}; + +static inline struct cx25840_ir_state *to_ir_state(struct v4l2_subdev *sd) +{ + struct cx25840_state *state = to_state(sd); + return state ? state->ir_state : NULL; +} + + +/* + * Rx and Tx Clock Divider register computations + * + * Note the largest clock divider value of 0xffff corresponds to: + * (0xffff + 1) * 1000 / 108/2 MHz = 1,213,629.629... ns + * which fits in 21 bits, so we'll use unsigned int for time arguments. + */ +static inline u16 count_to_clock_divider(unsigned int d) +{ + if (d > RXCLK_RCD + 1) + d = RXCLK_RCD; + else if (d < 2) + d = 1; + else + d--; + return (u16) d; +} + +static inline u16 ns_to_clock_divider(unsigned int ns) +{ + return count_to_clock_divider( + DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ / 1000000 * ns, 1000)); +} + +static inline unsigned int clock_divider_to_ns(unsigned int divider) +{ + /* Period of the Rx or Tx clock in ns */ + return DIV_ROUND_CLOSEST((divider + 1) * 1000, + CX25840_IR_REFCLK_FREQ / 1000000); +} + +static inline u16 carrier_freq_to_clock_divider(unsigned int freq) +{ + return count_to_clock_divider( + DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, freq * 16)); +} + +static inline unsigned int clock_divider_to_carrier_freq(unsigned int divider) +{ + return DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, (divider + 1) * 16); +} + +static inline u16 freq_to_clock_divider(unsigned int freq, + unsigned int rollovers) +{ + return count_to_clock_divider( + DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, freq * rollovers)); +} + +static inline unsigned int clock_divider_to_freq(unsigned int divider, + unsigned int rollovers) +{ + return DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, + (divider + 1) * rollovers); +} + +/* + * Low Pass Filter register calculations + * + * Note the largest count value of 0xffff corresponds to: + * 0xffff * 1000 / 108/2 MHz = 1,213,611.11... ns + * which fits in 21 bits, so we'll use unsigned int for time arguments. + */ +static inline u16 count_to_lpf_count(unsigned int d) +{ + if (d > FILTR_LPF) + d = FILTR_LPF; + else if (d < 4) + d = 0; + return (u16) d; +} + +static inline u16 ns_to_lpf_count(unsigned int ns) +{ + return count_to_lpf_count( + DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ / 1000000 * ns, 1000)); +} + +static inline unsigned int lpf_count_to_ns(unsigned int count) +{ + /* Duration of the Low Pass Filter rejection window in ns */ + return DIV_ROUND_CLOSEST(count * 1000, + CX25840_IR_REFCLK_FREQ / 1000000); +} + +static inline unsigned int lpf_count_to_us(unsigned int count) +{ + /* Duration of the Low Pass Filter rejection window in us */ + return DIV_ROUND_CLOSEST(count, CX25840_IR_REFCLK_FREQ / 1000000); +} + +/* + * FIFO register pulse width count compuations + */ +static u32 clock_divider_to_resolution(u16 divider) +{ + /* + * Resolution is the duration of 1 tick of the readable portion of + * of the pulse width counter as read from the FIFO. The two lsb's are + * not readable, hence the << 2. This function returns ns. + */ + return DIV_ROUND_CLOSEST((1 << 2) * ((u32) divider + 1) * 1000, + CX25840_IR_REFCLK_FREQ / 1000000); +} + +static u64 pulse_width_count_to_ns(u16 count, u16 divider) +{ + u64 n; + u32 rem; + + /* + * The 2 lsb's of the pulse width timer count are not readable, hence + * the (count << 2) | 0x3 + */ + n = (((u64) count << 2) | 0x3) * (divider + 1) * 1000; /* millicycles */ + rem = do_div(n, CX25840_IR_REFCLK_FREQ / 1000000); /* / MHz => ns */ + if (rem >= CX25840_IR_REFCLK_FREQ / 1000000 / 2) + n++; + return n; +} + +#if 0 +/* Keep as we will need this for Transmit functionality */ +static u16 ns_to_pulse_width_count(u32 ns, u16 divider) +{ + u64 n; + u32 d; + u32 rem; + + /* + * The 2 lsb's of the pulse width timer count are not accessible, hence + * the (1 << 2) + */ + n = ((u64) ns) * CX25840_IR_REFCLK_FREQ / 1000000; /* millicycles */ + d = (1 << 2) * ((u32) divider + 1) * 1000; /* millicycles/count */ + rem = do_div(n, d); + if (rem >= d / 2) + n++; + + if (n > FIFO_RXTX) + n = FIFO_RXTX; + else if (n == 0) + n = 1; + return (u16) n; +} + +#endif +static unsigned int pulse_width_count_to_us(u16 count, u16 divider) +{ + u64 n; + u32 rem; + + /* + * The 2 lsb's of the pulse width timer count are not readable, hence + * the (count << 2) | 0x3 + */ + n = (((u64) count << 2) | 0x3) * (divider + 1); /* cycles */ + rem = do_div(n, CX25840_IR_REFCLK_FREQ / 1000000); /* / MHz => us */ + if (rem >= CX25840_IR_REFCLK_FREQ / 1000000 / 2) + n++; + return (unsigned int) n; +} + +/* + * Pulse Clocks computations: Combined Pulse Width Count & Rx Clock Counts + * + * The total pulse clock count is an 18 bit pulse width timer count as the most + * significant part and (up to) 16 bit clock divider count as a modulus. + * When the Rx clock divider ticks down to 0, it increments the 18 bit pulse + * width timer count's least significant bit. + */ +static u64 ns_to_pulse_clocks(u32 ns) +{ + u64 clocks; + u32 rem; + clocks = CX25840_IR_REFCLK_FREQ / 1000000 * (u64) ns; /* millicycles */ + rem = do_div(clocks, 1000); /* /1000 = cycles */ + if (rem >= 1000 / 2) + clocks++; + return clocks; +} + +static u16 pulse_clocks_to_clock_divider(u64 count) +{ + do_div(count, (FIFO_RXTX << 2) | 0x3); + + /* net result needs to be rounded down and decremented by 1 */ + if (count > RXCLK_RCD + 1) + count = RXCLK_RCD; + else if (count < 2) + count = 1; + else + count--; + return (u16) count; +} + +/* + * IR Control Register helpers + */ +enum tx_fifo_watermark { + TX_FIFO_HALF_EMPTY = 0, + TX_FIFO_EMPTY = CNTRL_TIC, +}; + +enum rx_fifo_watermark { + RX_FIFO_HALF_FULL = 0, + RX_FIFO_NOT_EMPTY = CNTRL_RIC, +}; + +static inline void control_tx_irq_watermark(struct i2c_client *c, + enum tx_fifo_watermark level) +{ + cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_TIC, level); +} + +static inline void control_rx_irq_watermark(struct i2c_client *c, + enum rx_fifo_watermark level) +{ + cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_RIC, level); +} + +static inline void control_tx_enable(struct i2c_client *c, bool enable) +{ + cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~(CNTRL_TXE | CNTRL_TFE), + enable ? (CNTRL_TXE | CNTRL_TFE) : 0); +} + +static inline void control_rx_enable(struct i2c_client *c, bool enable) +{ + cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~(CNTRL_RXE | CNTRL_RFE), + enable ? (CNTRL_RXE | CNTRL_RFE) : 0); +} + +static inline void control_tx_modulation_enable(struct i2c_client *c, + bool enable) +{ + cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_MOD, + enable ? CNTRL_MOD : 0); +} + +static inline void control_rx_demodulation_enable(struct i2c_client *c, + bool enable) +{ + cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_DMD, + enable ? CNTRL_DMD : 0); +} + +static inline void control_rx_s_edge_detection(struct i2c_client *c, + u32 edge_types) +{ + cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_EDG_BOTH, + edge_types & CNTRL_EDG_BOTH); +} + +static void control_rx_s_carrier_window(struct i2c_client *c, + unsigned int carrier, + unsigned int *carrier_range_low, + unsigned int *carrier_range_high) +{ + u32 v; + unsigned int c16 = carrier * 16; + + if (*carrier_range_low < DIV_ROUND_CLOSEST(c16, 16 + 3)) { + v = CNTRL_WIN_3_4; + *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 4); + } else { + v = CNTRL_WIN_3_3; + *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 3); + } + + if (*carrier_range_high > DIV_ROUND_CLOSEST(c16, 16 - 3)) { + v |= CNTRL_WIN_4_3; + *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 4); + } else { + v |= CNTRL_WIN_3_3; + *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 3); + } + cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_WIN, v); +} + +static inline void control_tx_polarity_invert(struct i2c_client *c, + bool invert) +{ + cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_CPL, + invert ? CNTRL_CPL : 0); +} + +/* + * IR Rx & Tx Clock Register helpers + */ +static unsigned int txclk_tx_s_carrier(struct i2c_client *c, + unsigned int freq, + u16 *divider) +{ + *divider = carrier_freq_to_clock_divider(freq); + cx25840_write4(c, CX25840_IR_TXCLK_REG, *divider); + return clock_divider_to_carrier_freq(*divider); +} + +static unsigned int rxclk_rx_s_carrier(struct i2c_client *c, + unsigned int freq, + u16 *divider) +{ + *divider = carrier_freq_to_clock_divider(freq); + cx25840_write4(c, CX25840_IR_RXCLK_REG, *divider); + return clock_divider_to_carrier_freq(*divider); +} + +static u32 txclk_tx_s_max_pulse_width(struct i2c_client *c, u32 ns, + u16 *divider) +{ + u64 pulse_clocks; + + if (ns > IR_MAX_DURATION) + ns = IR_MAX_DURATION; + pulse_clocks = ns_to_pulse_clocks(ns); + *divider = pulse_clocks_to_clock_divider(pulse_clocks); + cx25840_write4(c, CX25840_IR_TXCLK_REG, *divider); + return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider); +} + +static u32 rxclk_rx_s_max_pulse_width(struct i2c_client *c, u32 ns, + u16 *divider) +{ + u64 pulse_clocks; + + if (ns > IR_MAX_DURATION) + ns = IR_MAX_DURATION; + pulse_clocks = ns_to_pulse_clocks(ns); + *divider = pulse_clocks_to_clock_divider(pulse_clocks); + cx25840_write4(c, CX25840_IR_RXCLK_REG, *divider); + return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider); +} + +/* + * IR Tx Carrier Duty Cycle register helpers + */ +static unsigned int cduty_tx_s_duty_cycle(struct i2c_client *c, + unsigned int duty_cycle) +{ + u32 n; + n = DIV_ROUND_CLOSEST(duty_cycle * 100, 625); /* 16ths of 100% */ + if (n != 0) + n--; + if (n > 15) + n = 15; + cx25840_write4(c, CX25840_IR_CDUTY_REG, n); + return DIV_ROUND_CLOSEST((n + 1) * 100, 16); +} + +/* + * IR Filter Register helpers + */ +static u32 filter_rx_s_min_width(struct i2c_client *c, u32 min_width_ns) +{ + u32 count = ns_to_lpf_count(min_width_ns); + cx25840_write4(c, CX25840_IR_FILTR_REG, count); + return lpf_count_to_ns(count); +} + +/* + * IR IRQ Enable Register helpers + */ +static inline void irqenable_rx(struct v4l2_subdev *sd, u32 mask) +{ + struct cx25840_state *state = to_state(sd); + + if (is_cx23885(state) || is_cx23887(state)) + mask ^= IRQEN_MSK; + mask &= (IRQEN_RTE | IRQEN_ROE | IRQEN_RSE); + cx25840_and_or4(state->c, CX25840_IR_IRQEN_REG, + ~(IRQEN_RTE | IRQEN_ROE | IRQEN_RSE), mask); +} + +static inline void irqenable_tx(struct v4l2_subdev *sd, u32 mask) +{ + struct cx25840_state *state = to_state(sd); + + if (is_cx23885(state) || is_cx23887(state)) + mask ^= IRQEN_MSK; + mask &= IRQEN_TSE; + cx25840_and_or4(state->c, CX25840_IR_IRQEN_REG, ~IRQEN_TSE, mask); +} + +/* + * V4L2 Subdevice IR Ops + */ +int cx25840_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled) +{ + struct cx25840_state *state = to_state(sd); + struct cx25840_ir_state *ir_state = to_ir_state(sd); + struct i2c_client *c = NULL; + unsigned long flags; + + union cx25840_ir_fifo_rec rx_data[FIFO_RX_DEPTH]; + unsigned int i, j, k; + u32 events, v; + int tsr, rsr, rto, ror, tse, rse, rte, roe, kror; + u32 cntrl, irqen, stats; + + *handled = false; + if (ir_state == NULL) + return -ENODEV; + + c = ir_state->c; + + /* Only support the IR controller for the CX2388[57] AV Core for now */ + if (!(is_cx23885(state) || is_cx23887(state))) + return -ENODEV; + + cntrl = cx25840_read4(c, CX25840_IR_CNTRL_REG); + irqen = cx25840_read4(c, CX25840_IR_IRQEN_REG); + if (is_cx23885(state) || is_cx23887(state)) + irqen ^= IRQEN_MSK; + stats = cx25840_read4(c, CX25840_IR_STATS_REG); + + tsr = stats & STATS_TSR; /* Tx FIFO Service Request */ + rsr = stats & STATS_RSR; /* Rx FIFO Service Request */ + rto = stats & STATS_RTO; /* Rx Pulse Width Timer Time Out */ + ror = stats & STATS_ROR; /* Rx FIFO Over Run */ + + tse = irqen & IRQEN_TSE; /* Tx FIFO Service Request IRQ Enable */ + rse = irqen & IRQEN_RSE; /* Rx FIFO Service Reuqest IRQ Enable */ + rte = irqen & IRQEN_RTE; /* Rx Pulse Width Timer Time Out IRQ Enable */ + roe = irqen & IRQEN_ROE; /* Rx FIFO Over Run IRQ Enable */ + + v4l2_dbg(2, ir_debug, sd, "IR IRQ Status: %s %s %s %s %s %s\n", + tsr ? "tsr" : " ", rsr ? "rsr" : " ", + rto ? "rto" : " ", ror ? "ror" : " ", + stats & STATS_TBY ? "tby" : " ", + stats & STATS_RBY ? "rby" : " "); + + v4l2_dbg(2, ir_debug, sd, "IR IRQ Enables: %s %s %s %s\n", + tse ? "tse" : " ", rse ? "rse" : " ", + rte ? "rte" : " ", roe ? "roe" : " "); + + /* + * Transmitter interrupt service + */ + if (tse && tsr) { + /* + * TODO: + * Check the watermark threshold setting + * Pull FIFO_TX_DEPTH or FIFO_TX_DEPTH/2 entries from tx_kfifo + * Push the data to the hardware FIFO. + * If there was nothing more to send in the tx_kfifo, disable + * the TSR IRQ and notify the v4l2_device. + * If there was something in the tx_kfifo, check the tx_kfifo + * level and notify the v4l2_device, if it is low. + */ + /* For now, inhibit TSR interrupt until Tx is implemented */ + irqenable_tx(sd, 0); + events = V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ; + v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_TX_NOTIFY, &events); + *handled = true; + } + + /* + * Receiver interrupt service + */ + kror = 0; + if ((rse && rsr) || (rte && rto)) { + /* + * Receive data on RSR to clear the STATS_RSR. + * Receive data on RTO, since we may not have yet hit the RSR + * watermark when we receive the RTO. + */ + for (i = 0, v = FIFO_RX_NDV; + (v & FIFO_RX_NDV) && !kror; i = 0) { + for (j = 0; + (v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) { + v = cx25840_read4(c, CX25840_IR_FIFO_REG); + rx_data[i].hw_fifo_data = v & ~FIFO_RX_NDV; + i++; + } + if (i == 0) + break; + j = i * sizeof(union cx25840_ir_fifo_rec); + k = kfifo_in_locked(&ir_state->rx_kfifo, + (unsigned char *) rx_data, j, + &ir_state->rx_kfifo_lock); + if (k != j) + kror++; /* rx_kfifo over run */ + } + *handled = true; + } + + events = 0; + v = 0; + if (kror) { + events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN; + v4l2_err(sd, "IR receiver software FIFO overrun\n"); + } + if (roe && ror) { + /* + * The RX FIFO Enable (CNTRL_RFE) must be toggled to clear + * the Rx FIFO Over Run status (STATS_ROR) + */ + v |= CNTRL_RFE; + events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN; + v4l2_err(sd, "IR receiver hardware FIFO overrun\n"); + } + if (rte && rto) { + /* + * The IR Receiver Enable (CNTRL_RXE) must be toggled to clear + * the Rx Pulse Width Timer Time Out (STATS_RTO) + */ + v |= CNTRL_RXE; + events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED; + } + if (v) { + /* Clear STATS_ROR & STATS_RTO as needed by reseting hardware */ + cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl & ~v); + cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl); + *handled = true; + } + spin_lock_irqsave(&ir_state->rx_kfifo_lock, flags); + if (kfifo_len(&ir_state->rx_kfifo) >= CX25840_IR_RX_KFIFO_SIZE / 2) + events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ; + spin_unlock_irqrestore(&ir_state->rx_kfifo_lock, flags); + + if (events) + v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_RX_NOTIFY, &events); + return 0; +} + +/* Receiver */ +static int cx25840_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count, + ssize_t *num) +{ + struct cx25840_ir_state *ir_state = to_ir_state(sd); + bool invert; + u16 divider; + unsigned int i, n; + union cx25840_ir_fifo_rec *p; + unsigned u, v, w; + + if (ir_state == NULL) + return -ENODEV; + + invert = (bool) atomic_read(&ir_state->rx_invert); + divider = (u16) atomic_read(&ir_state->rxclk_divider); + + n = count / sizeof(union cx25840_ir_fifo_rec) + * sizeof(union cx25840_ir_fifo_rec); + if (n == 0) { + *num = 0; + return 0; + } + + n = kfifo_out_locked(&ir_state->rx_kfifo, buf, n, + &ir_state->rx_kfifo_lock); + + n /= sizeof(union cx25840_ir_fifo_rec); + *num = n * sizeof(union cx25840_ir_fifo_rec); + + for (p = (union cx25840_ir_fifo_rec *) buf, i = 0; i < n; p++, i++) { + + if ((p->hw_fifo_data & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) { + /* Assume RTO was because of no IR light input */ + u = 0; + w = 1; + } else { + u = (p->hw_fifo_data & FIFO_RXTX_LVL) ? 1 : 0; + if (invert) + u = u ? 0 : 1; + w = 0; + } + + v = (unsigned) pulse_width_count_to_ns( + (u16) (p->hw_fifo_data & FIFO_RXTX), divider); + if (v > IR_MAX_DURATION) + v = IR_MAX_DURATION; + + init_ir_raw_event(&p->ir_core_data); + p->ir_core_data.pulse = u; + p->ir_core_data.duration = v; + p->ir_core_data.timeout = w; + + v4l2_dbg(2, ir_debug, sd, "rx read: %10u ns %s %s\n", + v, u ? "mark" : "space", w ? "(timed out)" : ""); + if (w) + v4l2_dbg(2, ir_debug, sd, "rx read: end of rx\n"); + } + return 0; +} + +static int cx25840_ir_rx_g_parameters(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *p) +{ + struct cx25840_ir_state *ir_state = to_ir_state(sd); + + if (ir_state == NULL) + return -ENODEV; + + mutex_lock(&ir_state->rx_params_lock); + memcpy(p, &ir_state->rx_params, + sizeof(struct v4l2_subdev_ir_parameters)); + mutex_unlock(&ir_state->rx_params_lock); + return 0; +} + +static int cx25840_ir_rx_shutdown(struct v4l2_subdev *sd) +{ + struct cx25840_ir_state *ir_state = to_ir_state(sd); + struct i2c_client *c; + + if (ir_state == NULL) + return -ENODEV; + + c = ir_state->c; + mutex_lock(&ir_state->rx_params_lock); + + /* Disable or slow down all IR Rx circuits and counters */ + irqenable_rx(sd, 0); + control_rx_enable(c, false); + control_rx_demodulation_enable(c, false); + control_rx_s_edge_detection(c, CNTRL_EDG_NONE); + filter_rx_s_min_width(c, 0); + cx25840_write4(c, CX25840_IR_RXCLK_REG, RXCLK_RCD); + + ir_state->rx_params.shutdown = true; + + mutex_unlock(&ir_state->rx_params_lock); + return 0; +} + +static int cx25840_ir_rx_s_parameters(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *p) +{ + struct cx25840_ir_state *ir_state = to_ir_state(sd); + struct i2c_client *c; + struct v4l2_subdev_ir_parameters *o; + u16 rxclk_divider; + + if (ir_state == NULL) + return -ENODEV; + + if (p->shutdown) + return cx25840_ir_rx_shutdown(sd); + + if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH) + return -ENOSYS; + + c = ir_state->c; + o = &ir_state->rx_params; + + mutex_lock(&ir_state->rx_params_lock); + + o->shutdown = p->shutdown; + + p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; + o->mode = p->mode; + + p->bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec); + o->bytes_per_data_element = p->bytes_per_data_element; + + /* Before we tweak the hardware, we have to disable the receiver */ + irqenable_rx(sd, 0); + control_rx_enable(c, false); + + control_rx_demodulation_enable(c, p->modulation); + o->modulation = p->modulation; + + if (p->modulation) { + p->carrier_freq = rxclk_rx_s_carrier(c, p->carrier_freq, + &rxclk_divider); + + o->carrier_freq = p->carrier_freq; + + p->duty_cycle = 50; + o->duty_cycle = p->duty_cycle; + + control_rx_s_carrier_window(c, p->carrier_freq, + &p->carrier_range_lower, + &p->carrier_range_upper); + o->carrier_range_lower = p->carrier_range_lower; + o->carrier_range_upper = p->carrier_range_upper; + + p->max_pulse_width = + (u32) pulse_width_count_to_ns(FIFO_RXTX, rxclk_divider); + } else { + p->max_pulse_width = + rxclk_rx_s_max_pulse_width(c, p->max_pulse_width, + &rxclk_divider); + } + o->max_pulse_width = p->max_pulse_width; + atomic_set(&ir_state->rxclk_divider, rxclk_divider); + + p->noise_filter_min_width = + filter_rx_s_min_width(c, p->noise_filter_min_width); + o->noise_filter_min_width = p->noise_filter_min_width; + + p->resolution = clock_divider_to_resolution(rxclk_divider); + o->resolution = p->resolution; + + /* FIXME - make this dependent on resolution for better performance */ + control_rx_irq_watermark(c, RX_FIFO_HALF_FULL); + + control_rx_s_edge_detection(c, CNTRL_EDG_BOTH); + + o->invert_level = p->invert_level; + atomic_set(&ir_state->rx_invert, p->invert_level); + + o->interrupt_enable = p->interrupt_enable; + o->enable = p->enable; + if (p->enable) { + unsigned long flags; + + spin_lock_irqsave(&ir_state->rx_kfifo_lock, flags); + kfifo_reset(&ir_state->rx_kfifo); + spin_unlock_irqrestore(&ir_state->rx_kfifo_lock, flags); + if (p->interrupt_enable) + irqenable_rx(sd, IRQEN_RSE | IRQEN_RTE | IRQEN_ROE); + control_rx_enable(c, p->enable); + } + + mutex_unlock(&ir_state->rx_params_lock); + return 0; +} + +/* Transmitter */ +static int cx25840_ir_tx_write(struct v4l2_subdev *sd, u8 *buf, size_t count, + ssize_t *num) +{ + struct cx25840_ir_state *ir_state = to_ir_state(sd); + + if (ir_state == NULL) + return -ENODEV; + +#if 0 + /* + * FIXME - the code below is an incomplete and untested sketch of what + * may need to be done. The critical part is to get 4 (or 8) pulses + * from the tx_kfifo, or converted from ns to the proper units from the + * input, and push them off to the hardware Tx FIFO right away, if the + * HW TX fifo needs service. The rest can be pushed to the tx_kfifo in + * a less critical timeframe. Also watch out for overruning the + * tx_kfifo - don't let it happen and let the caller know not all his + * pulses were written. + */ + u32 *ns_pulse = (u32 *) buf; + unsigned int n; + u32 fifo_pulse[FIFO_TX_DEPTH]; + u32 mark; + + /* Compute how much we can fit in the tx kfifo */ + n = CX25840_IR_TX_KFIFO_SIZE - kfifo_len(ir_state->tx_kfifo); + n = min(n, (unsigned int) count); + n /= sizeof(u32); + + /* FIXME - turn on Tx Fifo service interrupt + * check hardware fifo level, and other stuff + */ + for (i = 0; i < n; ) { + for (j = 0; j < FIFO_TX_DEPTH / 2 && i < n; j++) { + mark = ns_pulse[i] & LEVEL_MASK; + fifo_pulse[j] = ns_to_pulse_width_count( + ns_pulse[i] & + ~LEVEL_MASK, + ir_state->txclk_divider); + if (mark) + fifo_pulse[j] &= FIFO_RXTX_LVL; + i++; + } + kfifo_put(ir_state->tx_kfifo, (u8 *) fifo_pulse, + j * sizeof(u32)); + } + *num = n * sizeof(u32); +#else + /* For now enable the Tx FIFO Service interrupt & pretend we did work */ + irqenable_tx(sd, IRQEN_TSE); + *num = count; +#endif + return 0; +} + +static int cx25840_ir_tx_g_parameters(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *p) +{ + struct cx25840_ir_state *ir_state = to_ir_state(sd); + + if (ir_state == NULL) + return -ENODEV; + + mutex_lock(&ir_state->tx_params_lock); + memcpy(p, &ir_state->tx_params, + sizeof(struct v4l2_subdev_ir_parameters)); + mutex_unlock(&ir_state->tx_params_lock); + return 0; +} + +static int cx25840_ir_tx_shutdown(struct v4l2_subdev *sd) +{ + struct cx25840_ir_state *ir_state = to_ir_state(sd); + struct i2c_client *c; + + if (ir_state == NULL) + return -ENODEV; + + c = ir_state->c; + mutex_lock(&ir_state->tx_params_lock); + + /* Disable or slow down all IR Tx circuits and counters */ + irqenable_tx(sd, 0); + control_tx_enable(c, false); + control_tx_modulation_enable(c, false); + cx25840_write4(c, CX25840_IR_TXCLK_REG, TXCLK_TCD); + + ir_state->tx_params.shutdown = true; + + mutex_unlock(&ir_state->tx_params_lock); + return 0; +} + +static int cx25840_ir_tx_s_parameters(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *p) +{ + struct cx25840_ir_state *ir_state = to_ir_state(sd); + struct i2c_client *c; + struct v4l2_subdev_ir_parameters *o; + u16 txclk_divider; + + if (ir_state == NULL) + return -ENODEV; + + if (p->shutdown) + return cx25840_ir_tx_shutdown(sd); + + if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH) + return -ENOSYS; + + c = ir_state->c; + o = &ir_state->tx_params; + mutex_lock(&ir_state->tx_params_lock); + + o->shutdown = p->shutdown; + + p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; + o->mode = p->mode; + + p->bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec); + o->bytes_per_data_element = p->bytes_per_data_element; + + /* Before we tweak the hardware, we have to disable the transmitter */ + irqenable_tx(sd, 0); + control_tx_enable(c, false); + + control_tx_modulation_enable(c, p->modulation); + o->modulation = p->modulation; + + if (p->modulation) { + p->carrier_freq = txclk_tx_s_carrier(c, p->carrier_freq, + &txclk_divider); + o->carrier_freq = p->carrier_freq; + + p->duty_cycle = cduty_tx_s_duty_cycle(c, p->duty_cycle); + o->duty_cycle = p->duty_cycle; + + p->max_pulse_width = + (u32) pulse_width_count_to_ns(FIFO_RXTX, txclk_divider); + } else { + p->max_pulse_width = + txclk_tx_s_max_pulse_width(c, p->max_pulse_width, + &txclk_divider); + } + o->max_pulse_width = p->max_pulse_width; + atomic_set(&ir_state->txclk_divider, txclk_divider); + + p->resolution = clock_divider_to_resolution(txclk_divider); + o->resolution = p->resolution; + + /* FIXME - make this dependent on resolution for better performance */ + control_tx_irq_watermark(c, TX_FIFO_HALF_EMPTY); + + control_tx_polarity_invert(c, p->invert_carrier_sense); + o->invert_carrier_sense = p->invert_carrier_sense; + + /* + * FIXME: we don't have hardware help for IO pin level inversion + * here like we have on the CX23888. + * Act on this with some mix of logical inversion of data levels, + * carrier polarity, and carrier duty cycle. + */ + o->invert_level = p->invert_level; + + o->interrupt_enable = p->interrupt_enable; + o->enable = p->enable; + if (p->enable) { + /* reset tx_fifo here */ + if (p->interrupt_enable) + irqenable_tx(sd, IRQEN_TSE); + control_tx_enable(c, p->enable); + } + + mutex_unlock(&ir_state->tx_params_lock); + return 0; +} + + +/* + * V4L2 Subdevice Core Ops support + */ +int cx25840_ir_log_status(struct v4l2_subdev *sd) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *c = state->c; + char *s; + int i, j; + u32 cntrl, txclk, rxclk, cduty, stats, irqen, filtr; + + /* The CX23888 chip doesn't have an IR controller on the A/V core */ + if (is_cx23888(state)) + return 0; + + cntrl = cx25840_read4(c, CX25840_IR_CNTRL_REG); + txclk = cx25840_read4(c, CX25840_IR_TXCLK_REG) & TXCLK_TCD; + rxclk = cx25840_read4(c, CX25840_IR_RXCLK_REG) & RXCLK_RCD; + cduty = cx25840_read4(c, CX25840_IR_CDUTY_REG) & CDUTY_CDC; + stats = cx25840_read4(c, CX25840_IR_STATS_REG); + irqen = cx25840_read4(c, CX25840_IR_IRQEN_REG); + if (is_cx23885(state) || is_cx23887(state)) + irqen ^= IRQEN_MSK; + filtr = cx25840_read4(c, CX25840_IR_FILTR_REG) & FILTR_LPF; + + v4l2_info(sd, "IR Receiver:\n"); + v4l2_info(sd, "\tEnabled: %s\n", + cntrl & CNTRL_RXE ? "yes" : "no"); + v4l2_info(sd, "\tDemodulation from a carrier: %s\n", + cntrl & CNTRL_DMD ? "enabled" : "disabled"); + v4l2_info(sd, "\tFIFO: %s\n", + cntrl & CNTRL_RFE ? "enabled" : "disabled"); + switch (cntrl & CNTRL_EDG) { + case CNTRL_EDG_NONE: + s = "disabled"; + break; + case CNTRL_EDG_FALL: + s = "falling edge"; + break; + case CNTRL_EDG_RISE: + s = "rising edge"; + break; + case CNTRL_EDG_BOTH: + s = "rising & falling edges"; + break; + default: + s = "??? edge"; + break; + } + v4l2_info(sd, "\tPulse timers' start/stop trigger: %s\n", s); + v4l2_info(sd, "\tFIFO data on pulse timer overflow: %s\n", + cntrl & CNTRL_R ? "not loaded" : "overflow marker"); + v4l2_info(sd, "\tFIFO interrupt watermark: %s\n", + cntrl & CNTRL_RIC ? "not empty" : "half full or greater"); + v4l2_info(sd, "\tLoopback mode: %s\n", + cntrl & CNTRL_LBM ? "loopback active" : "normal receive"); + if (cntrl & CNTRL_DMD) { + v4l2_info(sd, "\tExpected carrier (16 clocks): %u Hz\n", + clock_divider_to_carrier_freq(rxclk)); + switch (cntrl & CNTRL_WIN) { + case CNTRL_WIN_3_3: + i = 3; + j = 3; + break; + case CNTRL_WIN_4_3: + i = 4; + j = 3; + break; + case CNTRL_WIN_3_4: + i = 3; + j = 4; + break; + case CNTRL_WIN_4_4: + i = 4; + j = 4; + break; + default: + i = 0; + j = 0; + break; + } + v4l2_info(sd, "\tNext carrier edge window: 16 clocks " + "-%1d/+%1d, %u to %u Hz\n", i, j, + clock_divider_to_freq(rxclk, 16 + j), + clock_divider_to_freq(rxclk, 16 - i)); + } + v4l2_info(sd, "\tMax measurable pulse width: %u us, %llu ns\n", + pulse_width_count_to_us(FIFO_RXTX, rxclk), + pulse_width_count_to_ns(FIFO_RXTX, rxclk)); + v4l2_info(sd, "\tLow pass filter: %s\n", + filtr ? "enabled" : "disabled"); + if (filtr) + v4l2_info(sd, "\tMin acceptable pulse width (LPF): %u us, " + "%u ns\n", + lpf_count_to_us(filtr), + lpf_count_to_ns(filtr)); + v4l2_info(sd, "\tPulse width timer timed-out: %s\n", + stats & STATS_RTO ? "yes" : "no"); + v4l2_info(sd, "\tPulse width timer time-out intr: %s\n", + irqen & IRQEN_RTE ? "enabled" : "disabled"); + v4l2_info(sd, "\tFIFO overrun: %s\n", + stats & STATS_ROR ? "yes" : "no"); + v4l2_info(sd, "\tFIFO overrun interrupt: %s\n", + irqen & IRQEN_ROE ? "enabled" : "disabled"); + v4l2_info(sd, "\tBusy: %s\n", + stats & STATS_RBY ? "yes" : "no"); + v4l2_info(sd, "\tFIFO service requested: %s\n", + stats & STATS_RSR ? "yes" : "no"); + v4l2_info(sd, "\tFIFO service request interrupt: %s\n", + irqen & IRQEN_RSE ? "enabled" : "disabled"); + + v4l2_info(sd, "IR Transmitter:\n"); + v4l2_info(sd, "\tEnabled: %s\n", + cntrl & CNTRL_TXE ? "yes" : "no"); + v4l2_info(sd, "\tModulation onto a carrier: %s\n", + cntrl & CNTRL_MOD ? "enabled" : "disabled"); + v4l2_info(sd, "\tFIFO: %s\n", + cntrl & CNTRL_TFE ? "enabled" : "disabled"); + v4l2_info(sd, "\tFIFO interrupt watermark: %s\n", + cntrl & CNTRL_TIC ? "not empty" : "half full or less"); + v4l2_info(sd, "\tCarrier polarity: %s\n", + cntrl & CNTRL_CPL ? "space:burst mark:noburst" + : "space:noburst mark:burst"); + if (cntrl & CNTRL_MOD) { + v4l2_info(sd, "\tCarrier (16 clocks): %u Hz\n", + clock_divider_to_carrier_freq(txclk)); + v4l2_info(sd, "\tCarrier duty cycle: %2u/16\n", + cduty + 1); + } + v4l2_info(sd, "\tMax pulse width: %u us, %llu ns\n", + pulse_width_count_to_us(FIFO_RXTX, txclk), + pulse_width_count_to_ns(FIFO_RXTX, txclk)); + v4l2_info(sd, "\tBusy: %s\n", + stats & STATS_TBY ? "yes" : "no"); + v4l2_info(sd, "\tFIFO service requested: %s\n", + stats & STATS_TSR ? "yes" : "no"); + v4l2_info(sd, "\tFIFO service request interrupt: %s\n", + irqen & IRQEN_TSE ? "enabled" : "disabled"); + + return 0; +} + + +const struct v4l2_subdev_ir_ops cx25840_ir_ops = { + .rx_read = cx25840_ir_rx_read, + .rx_g_parameters = cx25840_ir_rx_g_parameters, + .rx_s_parameters = cx25840_ir_rx_s_parameters, + + .tx_write = cx25840_ir_tx_write, + .tx_g_parameters = cx25840_ir_tx_g_parameters, + .tx_s_parameters = cx25840_ir_tx_s_parameters, +}; + + +static const struct v4l2_subdev_ir_parameters default_rx_params = { + .bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec), + .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, + + .enable = false, + .interrupt_enable = false, + .shutdown = true, + + .modulation = true, + .carrier_freq = 36000, /* 36 kHz - RC-5, and RC-6 carrier */ + + /* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */ + /* RC-6: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */ + .noise_filter_min_width = 333333, /* ns */ + .carrier_range_lower = 35000, + .carrier_range_upper = 37000, + .invert_level = false, +}; + +static const struct v4l2_subdev_ir_parameters default_tx_params = { + .bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec), + .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, + + .enable = false, + .interrupt_enable = false, + .shutdown = true, + + .modulation = true, + .carrier_freq = 36000, /* 36 kHz - RC-5 carrier */ + .duty_cycle = 25, /* 25 % - RC-5 carrier */ + .invert_level = false, + .invert_carrier_sense = false, +}; + +int cx25840_ir_probe(struct v4l2_subdev *sd) +{ + struct cx25840_state *state = to_state(sd); + struct cx25840_ir_state *ir_state; + struct v4l2_subdev_ir_parameters default_params; + + /* Only init the IR controller for the CX2388[57] AV Core for now */ + if (!(is_cx23885(state) || is_cx23887(state))) + return 0; + + ir_state = kzalloc(sizeof(struct cx25840_ir_state), GFP_KERNEL); + if (ir_state == NULL) + return -ENOMEM; + + spin_lock_init(&ir_state->rx_kfifo_lock); + if (kfifo_alloc(&ir_state->rx_kfifo, + CX25840_IR_RX_KFIFO_SIZE, GFP_KERNEL)) { + kfree(ir_state); + return -ENOMEM; + } + + ir_state->c = state->c; + state->ir_state = ir_state; + + /* Ensure no interrupts arrive yet */ + if (is_cx23885(state) || is_cx23887(state)) + cx25840_write4(ir_state->c, CX25840_IR_IRQEN_REG, IRQEN_MSK); + else + cx25840_write4(ir_state->c, CX25840_IR_IRQEN_REG, 0); + + mutex_init(&ir_state->rx_params_lock); + memcpy(&default_params, &default_rx_params, + sizeof(struct v4l2_subdev_ir_parameters)); + v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params); + + mutex_init(&ir_state->tx_params_lock); + memcpy(&default_params, &default_tx_params, + sizeof(struct v4l2_subdev_ir_parameters)); + v4l2_subdev_call(sd, ir, tx_s_parameters, &default_params); + + return 0; +} + +int cx25840_ir_remove(struct v4l2_subdev *sd) +{ + struct cx25840_state *state = to_state(sd); + struct cx25840_ir_state *ir_state = to_ir_state(sd); + + if (ir_state == NULL) + return -ENODEV; + + cx25840_ir_rx_shutdown(sd); + cx25840_ir_tx_shutdown(sd); + + kfifo_free(&ir_state->rx_kfifo); + kfree(ir_state); + state->ir_state = NULL; + return 0; +} diff --git a/drivers/media/i2c/cx25840/cx25840-vbi.c b/drivers/media/i2c/cx25840/cx25840-vbi.c new file mode 100644 index 00000000000..64a4004f8a9 --- /dev/null +++ b/drivers/media/i2c/cx25840/cx25840-vbi.c @@ -0,0 +1,256 @@ +/* cx25840 VBI functions + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include +#include +#include +#include + +#include "cx25840-core.h" + +static int odd_parity(u8 c) +{ + c ^= (c >> 4); + c ^= (c >> 2); + c ^= (c >> 1); + + return c & 1; +} + +static int decode_vps(u8 * dst, u8 * p) +{ + static const u8 biphase_tbl[] = { + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, + 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, + 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, + 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, + 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, + 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87, + 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3, + 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85, + 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1, + 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, + 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, + 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, + 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, + 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86, + 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2, + 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84, + 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0, + 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, + 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, + 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, + 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, + 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + }; + + u8 c, err = 0; + int i; + + for (i = 0; i < 2 * 13; i += 2) { + err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]]; + c = (biphase_tbl[p[i + 1]] & 0xf) | + ((biphase_tbl[p[i]] & 0xf) << 4); + dst[i / 2] = c; + } + + return err & 0xf0; +} + +int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct cx25840_state *state = to_state(sd); + static const u16 lcr2vbi[] = { + 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ + 0, V4L2_SLICED_WSS_625, 0, /* 4 */ + V4L2_SLICED_CAPTION_525, /* 6 */ + 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */ + 0, 0, 0, 0 + }; + int is_pal = !(state->std & V4L2_STD_525_60); + int i; + + memset(svbi, 0, sizeof(*svbi)); + /* we're done if raw VBI is active */ + if ((cx25840_read(client, 0x404) & 0x10) == 0) + return 0; + + if (is_pal) { + for (i = 7; i <= 23; i++) { + u8 v = cx25840_read(client, 0x424 + i - 7); + + svbi->service_lines[0][i] = lcr2vbi[v >> 4]; + svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; + svbi->service_set |= svbi->service_lines[0][i] | + svbi->service_lines[1][i]; + } + } else { + for (i = 10; i <= 21; i++) { + u8 v = cx25840_read(client, 0x424 + i - 10); + + svbi->service_lines[0][i] = lcr2vbi[v >> 4]; + svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; + svbi->service_set |= svbi->service_lines[0][i] | + svbi->service_lines[1][i]; + } + } + return 0; +} + +int cx25840_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct cx25840_state *state = to_state(sd); + int is_pal = !(state->std & V4L2_STD_525_60); + int vbi_offset = is_pal ? 1 : 0; + + /* Setup standard */ + cx25840_std_setup(client); + + /* VBI Offset */ + cx25840_write(client, 0x47f, vbi_offset); + cx25840_write(client, 0x404, 0x2e); + return 0; +} + +int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct cx25840_state *state = to_state(sd); + int is_pal = !(state->std & V4L2_STD_525_60); + int vbi_offset = is_pal ? 1 : 0; + int i, x; + u8 lcr[24]; + + for (x = 0; x <= 23; x++) + lcr[x] = 0x00; + + /* Setup standard */ + cx25840_std_setup(client); + + /* Sliced VBI */ + cx25840_write(client, 0x404, 0x32); /* Ancillary data */ + cx25840_write(client, 0x406, 0x13); + cx25840_write(client, 0x47f, vbi_offset); + + if (is_pal) { + for (i = 0; i <= 6; i++) + svbi->service_lines[0][i] = + svbi->service_lines[1][i] = 0; + } else { + for (i = 0; i <= 9; i++) + svbi->service_lines[0][i] = + svbi->service_lines[1][i] = 0; + + for (i = 22; i <= 23; i++) + svbi->service_lines[0][i] = + svbi->service_lines[1][i] = 0; + } + + for (i = 7; i <= 23; i++) { + for (x = 0; x <= 1; x++) { + switch (svbi->service_lines[1-x][i]) { + case V4L2_SLICED_TELETEXT_B: + lcr[i] |= 1 << (4 * x); + break; + case V4L2_SLICED_WSS_625: + lcr[i] |= 4 << (4 * x); + break; + case V4L2_SLICED_CAPTION_525: + lcr[i] |= 6 << (4 * x); + break; + case V4L2_SLICED_VPS: + lcr[i] |= 9 << (4 * x); + break; + } + } + } + + if (is_pal) { + for (x = 1, i = 0x424; i <= 0x434; i++, x++) + cx25840_write(client, i, lcr[6 + x]); + } else { + for (x = 1, i = 0x424; i <= 0x430; i++, x++) + cx25840_write(client, i, lcr[9 + x]); + for (i = 0x431; i <= 0x434; i++) + cx25840_write(client, i, 0); + } + + cx25840_write(client, 0x43c, 0x16); + cx25840_write(client, 0x474, is_pal ? 0x2a : 0x22); + return 0; +} + +int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi) +{ + struct cx25840_state *state = to_state(sd); + u8 *p = vbi->p; + int id1, id2, l, err = 0; + + if (p[0] || p[1] != 0xff || p[2] != 0xff || + (p[3] != 0x55 && p[3] != 0x91)) { + vbi->line = vbi->type = 0; + return 0; + } + + p += 4; + id1 = p[-1]; + id2 = p[0] & 0xf; + l = p[2] & 0x3f; + l += state->vbi_line_offset; + p += 4; + + switch (id2) { + case 1: + id2 = V4L2_SLICED_TELETEXT_B; + break; + case 4: + id2 = V4L2_SLICED_WSS_625; + break; + case 6: + id2 = V4L2_SLICED_CAPTION_525; + err = !odd_parity(p[0]) || !odd_parity(p[1]); + break; + case 9: + id2 = V4L2_SLICED_VPS; + if (decode_vps(p, p) != 0) + err = 1; + break; + default: + id2 = 0; + err = 1; + break; + } + + vbi->type = err ? 0 : id2; + vbi->line = err ? 0 : l; + vbi->is_second_field = err ? 0 : (id1 == 0x55); + vbi->p = p; + return 0; +} diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c new file mode 100644 index 00000000000..04f192a0398 --- /dev/null +++ b/drivers/media/i2c/ir-kbd-i2c.c @@ -0,0 +1,489 @@ +/* + * + * keyboard input driver for i2c IR remote controls + * + * Copyright (c) 2000-2003 Gerd Knorr + * modified for PixelView (BT878P+W/FM) by + * Michal Kochanowicz + * Christoph Bartelmus + * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by + * Ulrich Mueller + * modified for em2820 based USB TV tuners by + * Markus Rechberger + * modified for DViCO Fusion HDTV 5 RT GOLD by + * Chaogui Zhang + * modified for MSI TV@nywhere Plus by + * Henry Wong + * Mark Schultz + * Brian Rogers + * modified for AVerMedia Cardbus by + * Oldrich Jedlicka + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* ----------------------------------------------------------------------- */ +/* insmod parameters */ + +static int debug; +module_param(debug, int, 0644); /* debug level (0,1,2) */ + + +#define MODULE_NAME "ir-kbd-i2c" +#define dprintk(level, fmt, arg...) if (debug >= level) \ + printk(KERN_DEBUG MODULE_NAME ": " fmt , ## arg) + +/* ----------------------------------------------------------------------- */ + +static int get_key_haup_common(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, + int size, int offset) +{ + unsigned char buf[6]; + int start, range, toggle, dev, code, ircode; + + /* poll IR chip */ + if (size != i2c_master_recv(ir->c, buf, size)) + return -EIO; + + /* split rc5 data block ... */ + start = (buf[offset] >> 7) & 1; + range = (buf[offset] >> 6) & 1; + toggle = (buf[offset] >> 5) & 1; + dev = buf[offset] & 0x1f; + code = (buf[offset+1] >> 2) & 0x3f; + + /* rc5 has two start bits + * the first bit must be one + * the second bit defines the command range (1 = 0-63, 0 = 64 - 127) + */ + if (!start) + /* no key pressed */ + return 0; + /* + * Hauppauge remotes (black/silver) always use + * specific device ids. If we do not filter the + * device ids then messages destined for devices + * such as TVs (id=0) will get through causing + * mis-fired events. + * + * We also filter out invalid key presses which + * produce annoying debug log entries. + */ + ircode= (start << 12) | (toggle << 11) | (dev << 6) | code; + if ((ircode & 0x1fff)==0x1fff) + /* invalid key press */ + return 0; + + if (!range) + code += 64; + + dprintk(1,"ir hauppauge (rc5): s%d r%d t%d dev=%d code=%d\n", + start, range, toggle, dev, code); + + /* return key */ + *ir_key = (dev << 8) | code; + *ir_raw = ircode; + return 1; +} + +static int get_key_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +{ + return get_key_haup_common (ir, ir_key, ir_raw, 3, 0); +} + +static int get_key_haup_xvr(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +{ + int ret; + unsigned char buf[1] = { 0 }; + + /* + * This is the same apparent "are you ready?" poll command observed + * watching Windows driver traffic and implemented in lirc_zilog. With + * this added, we get far saner remote behavior with z8 chips on usb + * connected devices, even with the default polling interval of 100ms. + */ + ret = i2c_master_send(ir->c, buf, 1); + if (ret != 1) + return (ret < 0) ? ret : -EINVAL; + + return get_key_haup_common (ir, ir_key, ir_raw, 6, 3); +} + +static int get_key_pixelview(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)) { + dprintk(1,"read error\n"); + return -EIO; + } + *ir_key = b; + *ir_raw = b; + return 1; +} + +static int get_key_fusionhdtv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +{ + unsigned char buf[4]; + + /* poll IR chip */ + if (4 != i2c_master_recv(ir->c, buf, 4)) { + dprintk(1,"read error\n"); + return -EIO; + } + + if(buf[0] !=0 || buf[1] !=0 || buf[2] !=0 || buf[3] != 0) + dprintk(2, "%s: 0x%2x 0x%2x 0x%2x 0x%2x\n", __func__, + buf[0], buf[1], buf[2], buf[3]); + + /* no key pressed or signal from other ir remote */ + if(buf[0] != 0x1 || buf[1] != 0xfe) + return 0; + + *ir_key = buf[2]; + *ir_raw = (buf[2] << 8) | buf[3]; + + return 1; +} + +static int get_key_knc1(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)) { + dprintk(1,"read error\n"); + return -EIO; + } + + /* it seems that 0xFE indicates that a button is still hold + down, while 0xff indicates that no button is hold + down. 0xfe sequences are sometimes interrupted by 0xFF */ + + dprintk(2,"key %02x\n", b); + + if (b == 0xff) + return 0; + + if (b == 0xfe) + /* keep old data */ + return 1; + + *ir_key = b; + *ir_raw = b; + return 1; +} + +static int get_key_avermedia_cardbus(struct IR_i2c *ir, + u32 *ir_key, u32 *ir_raw) +{ + unsigned char subaddr, key, keygroup; + struct i2c_msg msg[] = { { .addr = ir->c->addr, .flags = 0, + .buf = &subaddr, .len = 1}, + { .addr = ir->c->addr, .flags = I2C_M_RD, + .buf = &key, .len = 1} }; + subaddr = 0x0d; + if (2 != i2c_transfer(ir->c->adapter, msg, 2)) { + dprintk(1, "read error\n"); + return -EIO; + } + + if (key == 0xff) + return 0; + + subaddr = 0x0b; + msg[1].buf = &keygroup; + if (2 != i2c_transfer(ir->c->adapter, msg, 2)) { + dprintk(1, "read error\n"); + return -EIO; + } + + if (keygroup == 0xff) + return 0; + + dprintk(1, "read key 0x%02x/0x%02x\n", key, keygroup); + if (keygroup < 2 || keygroup > 3) { + /* Only a warning */ + dprintk(1, "warning: invalid key group 0x%02x for key 0x%02x\n", + keygroup, key); + } + key |= (keygroup & 1) << 6; + + *ir_key = key; + *ir_raw = key; + return 1; +} + +/* ----------------------------------------------------------------------- */ + +static int ir_key_poll(struct IR_i2c *ir) +{ + static u32 ir_key, ir_raw; + int rc; + + dprintk(3, "%s\n", __func__); + rc = ir->get_key(ir, &ir_key, &ir_raw); + if (rc < 0) { + dprintk(2,"error\n"); + return rc; + } + + if (rc) { + dprintk(1, "%s: keycode = 0x%04x\n", __func__, ir_key); + rc_keydown(ir->rc, ir_key, 0); + } + return 0; +} + +static void ir_work(struct work_struct *work) +{ + int rc; + struct IR_i2c *ir = container_of(work, struct IR_i2c, work.work); + + rc = ir_key_poll(ir); + if (rc == -ENODEV) { + rc_unregister_device(ir->rc); + ir->rc = NULL; + return; + } + + schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling_interval)); +} + +/* ----------------------------------------------------------------------- */ + +static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + char *ir_codes = NULL; + const char *name = NULL; + u64 rc_type = RC_TYPE_UNKNOWN; + struct IR_i2c *ir; + struct rc_dev *rc = NULL; + struct i2c_adapter *adap = client->adapter; + unsigned short addr = client->addr; + int err; + + ir = kzalloc(sizeof(struct IR_i2c), GFP_KERNEL); + if (!ir) + return -ENOMEM; + + ir->c = client; + ir->polling_interval = DEFAULT_POLLING_INTERVAL; + i2c_set_clientdata(client, ir); + + switch(addr) { + case 0x64: + name = "Pixelview"; + ir->get_key = get_key_pixelview; + rc_type = RC_TYPE_OTHER; + ir_codes = RC_MAP_EMPTY; + break; + case 0x18: + case 0x1f: + case 0x1a: + name = "Hauppauge"; + ir->get_key = get_key_haup; + rc_type = RC_TYPE_RC5; + ir_codes = RC_MAP_HAUPPAUGE; + break; + case 0x30: + name = "KNC One"; + ir->get_key = get_key_knc1; + rc_type = RC_TYPE_OTHER; + ir_codes = RC_MAP_EMPTY; + break; + case 0x6b: + name = "FusionHDTV"; + ir->get_key = get_key_fusionhdtv; + rc_type = RC_TYPE_RC5; + ir_codes = RC_MAP_FUSIONHDTV_MCE; + break; + case 0x40: + name = "AVerMedia Cardbus remote"; + ir->get_key = get_key_avermedia_cardbus; + rc_type = RC_TYPE_OTHER; + ir_codes = RC_MAP_AVERMEDIA_CARDBUS; + break; + case 0x71: + name = "Hauppauge/Zilog Z8"; + ir->get_key = get_key_haup_xvr; + rc_type = RC_TYPE_RC5; + ir_codes = RC_MAP_HAUPPAUGE; + break; + } + + /* Let the caller override settings */ + if (client->dev.platform_data) { + const struct IR_i2c_init_data *init_data = + client->dev.platform_data; + + ir_codes = init_data->ir_codes; + rc = init_data->rc_dev; + + name = init_data->name; + if (init_data->type) + rc_type = init_data->type; + + if (init_data->polling_interval) + ir->polling_interval = init_data->polling_interval; + + switch (init_data->internal_get_key_func) { + case IR_KBD_GET_KEY_CUSTOM: + /* The bridge driver provided us its own function */ + ir->get_key = init_data->get_key; + break; + case IR_KBD_GET_KEY_PIXELVIEW: + ir->get_key = get_key_pixelview; + break; + case IR_KBD_GET_KEY_HAUP: + ir->get_key = get_key_haup; + break; + case IR_KBD_GET_KEY_KNC1: + ir->get_key = get_key_knc1; + break; + case IR_KBD_GET_KEY_FUSIONHDTV: + ir->get_key = get_key_fusionhdtv; + break; + case IR_KBD_GET_KEY_HAUP_XVR: + ir->get_key = get_key_haup_xvr; + break; + case IR_KBD_GET_KEY_AVERMEDIA_CARDBUS: + ir->get_key = get_key_avermedia_cardbus; + break; + } + } + + if (!rc) { + /* + * If platform_data doesn't specify rc_dev, initilize it + * internally + */ + rc = rc_allocate_device(); + if (!rc) { + err = -ENOMEM; + goto err_out_free; + } + } + ir->rc = rc; + + /* Make sure we are all setup before going on */ + if (!name || !ir->get_key || !rc_type || !ir_codes) { + dprintk(1, ": Unsupported device at address 0x%02x\n", + addr); + err = -ENODEV; + goto err_out_free; + } + + /* Sets name */ + snprintf(ir->name, sizeof(ir->name), "i2c IR (%s)", name); + ir->ir_codes = ir_codes; + + snprintf(ir->phys, sizeof(ir->phys), "%s/%s/ir0", + dev_name(&adap->dev), + dev_name(&client->dev)); + + /* + * Initialize input_dev fields + * It doesn't make sense to allow overriding them via platform_data + */ + rc->input_id.bustype = BUS_I2C; + rc->input_phys = ir->phys; + rc->input_name = ir->name; + + /* + * Initialize the other fields of rc_dev + */ + rc->map_name = ir->ir_codes; + rc->allowed_protos = rc_type; + if (!rc->driver_name) + rc->driver_name = MODULE_NAME; + + err = rc_register_device(rc); + if (err) + goto err_out_free; + + printk(MODULE_NAME ": %s detected at %s [%s]\n", + ir->name, ir->phys, adap->name); + + /* start polling via eventd */ + INIT_DELAYED_WORK(&ir->work, ir_work); + schedule_delayed_work(&ir->work, 0); + + return 0; + + err_out_free: + /* Only frees rc if it were allocated internally */ + rc_free_device(rc); + kfree(ir); + return err; +} + +static int ir_remove(struct i2c_client *client) +{ + struct IR_i2c *ir = i2c_get_clientdata(client); + + /* kill outstanding polls */ + cancel_delayed_work_sync(&ir->work); + + /* unregister device */ + if (ir->rc) + rc_unregister_device(ir->rc); + + /* free memory */ + kfree(ir); + return 0; +} + +static const struct i2c_device_id ir_kbd_id[] = { + /* Generic entry for any IR receiver */ + { "ir_video", 0 }, + /* IR device specific entries should be added here */ + { "ir_rx_z8f0811_haup", 0 }, + { "ir_rx_z8f0811_hdpvr", 0 }, + { } +}; + +static struct i2c_driver ir_kbd_driver = { + .driver = { + .name = "ir-kbd-i2c", + }, + .probe = ir_probe, + .remove = ir_remove, + .id_table = ir_kbd_id, +}; + +module_i2c_driver(ir_kbd_driver); + +/* ----------------------------------------------------------------------- */ + +MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, Ulrich Mueller"); +MODULE_DESCRIPTION("input driver for i2c IR remote controls"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/ks0127.c b/drivers/media/i2c/ks0127.c new file mode 100644 index 00000000000..ee7ca2dcca2 --- /dev/null +++ b/drivers/media/i2c/ks0127.c @@ -0,0 +1,724 @@ +/* + * Video Capture Driver (Video for Linux 1/2) + * for the Matrox Marvel G200,G400 and Rainbow Runner-G series + * + * This module is an interface to the KS0127 video decoder chip. + * + * Copyright (C) 1999 Ryan Drake + * + * 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. + * + ***************************************************************************** + * + * Modified and extended by + * Mike Bernson + * Gerard v.d. Horst + * Leon van Stuivenberg + * Gernot Ziegler + * + * Version History: + * V1.0 Ryan Drake Initial version by Ryan Drake + * V1.1 Gerard v.d. Horst Added some debugoutput, reset the video-standard + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ks0127.h" + +MODULE_DESCRIPTION("KS0127 video decoder driver"); +MODULE_AUTHOR("Ryan Drake"); +MODULE_LICENSE("GPL"); + +/* Addresses */ +#define I2C_KS0127_ADDON 0xD8 +#define I2C_KS0127_ONBOARD 0xDA + + +/* ks0127 control registers */ +#define KS_STAT 0x00 +#define KS_CMDA 0x01 +#define KS_CMDB 0x02 +#define KS_CMDC 0x03 +#define KS_CMDD 0x04 +#define KS_HAVB 0x05 +#define KS_HAVE 0x06 +#define KS_HS1B 0x07 +#define KS_HS1E 0x08 +#define KS_HS2B 0x09 +#define KS_HS2E 0x0a +#define KS_AGC 0x0b +#define KS_HXTRA 0x0c +#define KS_CDEM 0x0d +#define KS_PORTAB 0x0e +#define KS_LUMA 0x0f +#define KS_CON 0x10 +#define KS_BRT 0x11 +#define KS_CHROMA 0x12 +#define KS_CHROMB 0x13 +#define KS_DEMOD 0x14 +#define KS_SAT 0x15 +#define KS_HUE 0x16 +#define KS_VERTIA 0x17 +#define KS_VERTIB 0x18 +#define KS_VERTIC 0x19 +#define KS_HSCLL 0x1a +#define KS_HSCLH 0x1b +#define KS_VSCLL 0x1c +#define KS_VSCLH 0x1d +#define KS_OFMTA 0x1e +#define KS_OFMTB 0x1f +#define KS_VBICTL 0x20 +#define KS_CCDAT2 0x21 +#define KS_CCDAT1 0x22 +#define KS_VBIL30 0x23 +#define KS_VBIL74 0x24 +#define KS_VBIL118 0x25 +#define KS_VBIL1512 0x26 +#define KS_TTFRAM 0x27 +#define KS_TESTA 0x28 +#define KS_UVOFFH 0x29 +#define KS_UVOFFL 0x2a +#define KS_UGAIN 0x2b +#define KS_VGAIN 0x2c +#define KS_VAVB 0x2d +#define KS_VAVE 0x2e +#define KS_CTRACK 0x2f +#define KS_POLCTL 0x30 +#define KS_REFCOD 0x31 +#define KS_INVALY 0x32 +#define KS_INVALU 0x33 +#define KS_INVALV 0x34 +#define KS_UNUSEY 0x35 +#define KS_UNUSEU 0x36 +#define KS_UNUSEV 0x37 +#define KS_USRSAV 0x38 +#define KS_USREAV 0x39 +#define KS_SHS1A 0x3a +#define KS_SHS1B 0x3b +#define KS_SHS1C 0x3c +#define KS_CMDE 0x3d +#define KS_VSDEL 0x3e +#define KS_CMDF 0x3f +#define KS_GAMMA0 0x40 +#define KS_GAMMA1 0x41 +#define KS_GAMMA2 0x42 +#define KS_GAMMA3 0x43 +#define KS_GAMMA4 0x44 +#define KS_GAMMA5 0x45 +#define KS_GAMMA6 0x46 +#define KS_GAMMA7 0x47 +#define KS_GAMMA8 0x48 +#define KS_GAMMA9 0x49 +#define KS_GAMMA10 0x4a +#define KS_GAMMA11 0x4b +#define KS_GAMMA12 0x4c +#define KS_GAMMA13 0x4d +#define KS_GAMMA14 0x4e +#define KS_GAMMA15 0x4f +#define KS_GAMMA16 0x50 +#define KS_GAMMA17 0x51 +#define KS_GAMMA18 0x52 +#define KS_GAMMA19 0x53 +#define KS_GAMMA20 0x54 +#define KS_GAMMA21 0x55 +#define KS_GAMMA22 0x56 +#define KS_GAMMA23 0x57 +#define KS_GAMMA24 0x58 +#define KS_GAMMA25 0x59 +#define KS_GAMMA26 0x5a +#define KS_GAMMA27 0x5b +#define KS_GAMMA28 0x5c +#define KS_GAMMA29 0x5d +#define KS_GAMMA30 0x5e +#define KS_GAMMA31 0x5f +#define KS_GAMMAD0 0x60 +#define KS_GAMMAD1 0x61 +#define KS_GAMMAD2 0x62 +#define KS_GAMMAD3 0x63 +#define KS_GAMMAD4 0x64 +#define KS_GAMMAD5 0x65 +#define KS_GAMMAD6 0x66 +#define KS_GAMMAD7 0x67 +#define KS_GAMMAD8 0x68 +#define KS_GAMMAD9 0x69 +#define KS_GAMMAD10 0x6a +#define KS_GAMMAD11 0x6b +#define KS_GAMMAD12 0x6c +#define KS_GAMMAD13 0x6d +#define KS_GAMMAD14 0x6e +#define KS_GAMMAD15 0x6f +#define KS_GAMMAD16 0x70 +#define KS_GAMMAD17 0x71 +#define KS_GAMMAD18 0x72 +#define KS_GAMMAD19 0x73 +#define KS_GAMMAD20 0x74 +#define KS_GAMMAD21 0x75 +#define KS_GAMMAD22 0x76 +#define KS_GAMMAD23 0x77 +#define KS_GAMMAD24 0x78 +#define KS_GAMMAD25 0x79 +#define KS_GAMMAD26 0x7a +#define KS_GAMMAD27 0x7b +#define KS_GAMMAD28 0x7c +#define KS_GAMMAD29 0x7d +#define KS_GAMMAD30 0x7e +#define KS_GAMMAD31 0x7f + + +/**************************************************************************** +* mga_dev : represents one ks0127 chip. +****************************************************************************/ + +struct adjust { + int contrast; + int bright; + int hue; + int ugain; + int vgain; +}; + +struct ks0127 { + struct v4l2_subdev sd; + v4l2_std_id norm; + int ident; + u8 regs[256]; +}; + +static inline struct ks0127 *to_ks0127(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ks0127, sd); +} + + +static int debug; /* insmod parameter */ + +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug output"); + +static u8 reg_defaults[64]; + +static void init_reg_defaults(void) +{ + static int initialized; + u8 *table = reg_defaults; + + if (initialized) + return; + initialized = 1; + + table[KS_CMDA] = 0x2c; /* VSE=0, CCIR 601, autodetect standard */ + table[KS_CMDB] = 0x12; /* VALIGN=0, AGC control and input */ + table[KS_CMDC] = 0x00; /* Test options */ + /* clock & input select, write 1 to PORTA */ + table[KS_CMDD] = 0x01; + table[KS_HAVB] = 0x00; /* HAV Start Control */ + table[KS_HAVE] = 0x00; /* HAV End Control */ + table[KS_HS1B] = 0x10; /* HS1 Start Control */ + table[KS_HS1E] = 0x00; /* HS1 End Control */ + table[KS_HS2B] = 0x00; /* HS2 Start Control */ + table[KS_HS2E] = 0x00; /* HS2 End Control */ + table[KS_AGC] = 0x53; /* Manual setting for AGC */ + table[KS_HXTRA] = 0x00; /* Extra Bits for HAV and HS1/2 */ + table[KS_CDEM] = 0x00; /* Chroma Demodulation Control */ + table[KS_PORTAB] = 0x0f; /* port B is input, port A output GPPORT */ + table[KS_LUMA] = 0x01; /* Luma control */ + table[KS_CON] = 0x00; /* Contrast Control */ + table[KS_BRT] = 0x00; /* Brightness Control */ + table[KS_CHROMA] = 0x2a; /* Chroma control A */ + table[KS_CHROMB] = 0x90; /* Chroma control B */ + table[KS_DEMOD] = 0x00; /* Chroma Demodulation Control & Status */ + table[KS_SAT] = 0x00; /* Color Saturation Control*/ + table[KS_HUE] = 0x00; /* Hue Control */ + table[KS_VERTIA] = 0x00; /* Vertical Processing Control A */ + /* Vertical Processing Control B, luma 1 line delayed */ + table[KS_VERTIB] = 0x12; + table[KS_VERTIC] = 0x0b; /* Vertical Processing Control C */ + table[KS_HSCLL] = 0x00; /* Horizontal Scaling Ratio Low */ + table[KS_HSCLH] = 0x00; /* Horizontal Scaling Ratio High */ + table[KS_VSCLL] = 0x00; /* Vertical Scaling Ratio Low */ + table[KS_VSCLH] = 0x00; /* Vertical Scaling Ratio High */ + /* 16 bit YCbCr 4:2:2 output; I can't make the bt866 like 8 bit /Sam */ + table[KS_OFMTA] = 0x30; + table[KS_OFMTB] = 0x00; /* Output Control B */ + /* VBI Decoder Control; 4bit fmt: avoid Y overflow */ + table[KS_VBICTL] = 0x5d; + table[KS_CCDAT2] = 0x00; /* Read Only register */ + table[KS_CCDAT1] = 0x00; /* Read Only register */ + table[KS_VBIL30] = 0xa8; /* VBI data decoding options */ + table[KS_VBIL74] = 0xaa; /* VBI data decoding options */ + table[KS_VBIL118] = 0x2a; /* VBI data decoding options */ + table[KS_VBIL1512] = 0x00; /* VBI data decoding options */ + table[KS_TTFRAM] = 0x00; /* Teletext frame alignment pattern */ + table[KS_TESTA] = 0x00; /* test register, shouldn't be written */ + table[KS_UVOFFH] = 0x00; /* UV Offset Adjustment High */ + table[KS_UVOFFL] = 0x00; /* UV Offset Adjustment Low */ + table[KS_UGAIN] = 0x00; /* U Component Gain Adjustment */ + table[KS_VGAIN] = 0x00; /* V Component Gain Adjustment */ + table[KS_VAVB] = 0x07; /* VAV Begin */ + table[KS_VAVE] = 0x00; /* VAV End */ + table[KS_CTRACK] = 0x00; /* Chroma Tracking Control */ + table[KS_POLCTL] = 0x41; /* Timing Signal Polarity Control */ + table[KS_REFCOD] = 0x80; /* Reference Code Insertion Control */ + table[KS_INVALY] = 0x10; /* Invalid Y Code */ + table[KS_INVALU] = 0x80; /* Invalid U Code */ + table[KS_INVALV] = 0x80; /* Invalid V Code */ + table[KS_UNUSEY] = 0x10; /* Unused Y Code */ + table[KS_UNUSEU] = 0x80; /* Unused U Code */ + table[KS_UNUSEV] = 0x80; /* Unused V Code */ + table[KS_USRSAV] = 0x00; /* reserved */ + table[KS_USREAV] = 0x00; /* reserved */ + table[KS_SHS1A] = 0x00; /* User Defined SHS1 A */ + /* User Defined SHS1 B, ALT656=1 on 0127B */ + table[KS_SHS1B] = 0x80; + table[KS_SHS1C] = 0x00; /* User Defined SHS1 C */ + table[KS_CMDE] = 0x00; /* Command Register E */ + table[KS_VSDEL] = 0x00; /* VS Delay Control */ + /* Command Register F, update -immediately- */ + /* (there might come no vsync)*/ + table[KS_CMDF] = 0x02; +} + + +/* We need to manually read because of a bug in the KS0127 chip. + * + * An explanation from kayork@mail.utexas.edu: + * + * During I2C reads, the KS0127 only samples for a stop condition + * during the place where the acknowledge bit should be. Any standard + * I2C implementation (correctly) throws in another clock transition + * at the 9th bit, and the KS0127 will not recognize the stop condition + * and will continue to clock out data. + * + * So we have to do the read ourself. Big deal. + * workaround in i2c-algo-bit + */ + + +static u8 ks0127_read(struct v4l2_subdev *sd, u8 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + char val = 0; + struct i2c_msg msgs[] = { + { client->addr, 0, sizeof(reg), ® }, + { client->addr, I2C_M_RD | I2C_M_NO_RD_ACK, sizeof(val), &val } + }; + int ret; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + v4l2_dbg(1, debug, sd, "read error\n"); + + return val; +} + + +static void ks0127_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ks0127 *ks = to_ks0127(sd); + char msg[] = { reg, val }; + + if (i2c_master_send(client, msg, sizeof(msg)) != sizeof(msg)) + v4l2_dbg(1, debug, sd, "write error\n"); + + ks->regs[reg] = val; +} + + +/* generic bit-twiddling */ +static void ks0127_and_or(struct v4l2_subdev *sd, u8 reg, u8 and_v, u8 or_v) +{ + struct ks0127 *ks = to_ks0127(sd); + + u8 val = ks->regs[reg]; + val = (val & and_v) | or_v; + ks0127_write(sd, reg, val); +} + + + +/**************************************************************************** +* ks0127 private api +****************************************************************************/ +static void ks0127_init(struct v4l2_subdev *sd) +{ + struct ks0127 *ks = to_ks0127(sd); + u8 *table = reg_defaults; + int i; + + ks->ident = V4L2_IDENT_KS0127; + + v4l2_dbg(1, debug, sd, "reset\n"); + msleep(1); + + /* initialize all registers to known values */ + /* (except STAT, 0x21, 0x22, TEST and 0x38,0x39) */ + + for (i = 1; i < 33; i++) + ks0127_write(sd, i, table[i]); + + for (i = 35; i < 40; i++) + ks0127_write(sd, i, table[i]); + + for (i = 41; i < 56; i++) + ks0127_write(sd, i, table[i]); + + for (i = 58; i < 64; i++) + ks0127_write(sd, i, table[i]); + + + if ((ks0127_read(sd, KS_STAT) & 0x80) == 0) { + ks->ident = V4L2_IDENT_KS0122S; + v4l2_dbg(1, debug, sd, "ks0122s found\n"); + return; + } + + switch (ks0127_read(sd, KS_CMDE) & 0x0f) { + case 0: + v4l2_dbg(1, debug, sd, "ks0127 found\n"); + break; + + case 9: + ks->ident = V4L2_IDENT_KS0127B; + v4l2_dbg(1, debug, sd, "ks0127B Revision A found\n"); + break; + + default: + v4l2_dbg(1, debug, sd, "unknown revision\n"); + break; + } +} + +static int ks0127_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct ks0127 *ks = to_ks0127(sd); + + switch (input) { + case KS_INPUT_COMPOSITE_1: + case KS_INPUT_COMPOSITE_2: + case KS_INPUT_COMPOSITE_3: + case KS_INPUT_COMPOSITE_4: + case KS_INPUT_COMPOSITE_5: + case KS_INPUT_COMPOSITE_6: + v4l2_dbg(1, debug, sd, + "s_routing %d: Composite\n", input); + /* autodetect 50/60 Hz */ + ks0127_and_or(sd, KS_CMDA, 0xfc, 0x00); + /* VSE=0 */ + ks0127_and_or(sd, KS_CMDA, ~0x40, 0x00); + /* set input line */ + ks0127_and_or(sd, KS_CMDB, 0xb0, input); + /* non-freerunning mode */ + ks0127_and_or(sd, KS_CMDC, 0x70, 0x0a); + /* analog input */ + ks0127_and_or(sd, KS_CMDD, 0x03, 0x00); + /* enable chroma demodulation */ + ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x00); + /* chroma trap, HYBWR=1 */ + ks0127_and_or(sd, KS_LUMA, 0x00, + (reg_defaults[KS_LUMA])|0x0c); + /* scaler fullbw, luma comb off */ + ks0127_and_or(sd, KS_VERTIA, 0x08, 0x81); + /* manual chroma comb .25 .5 .25 */ + ks0127_and_or(sd, KS_VERTIC, 0x0f, 0x90); + + /* chroma path delay */ + ks0127_and_or(sd, KS_CHROMB, 0x0f, 0x90); + + ks0127_write(sd, KS_UGAIN, reg_defaults[KS_UGAIN]); + ks0127_write(sd, KS_VGAIN, reg_defaults[KS_VGAIN]); + ks0127_write(sd, KS_UVOFFH, reg_defaults[KS_UVOFFH]); + ks0127_write(sd, KS_UVOFFL, reg_defaults[KS_UVOFFL]); + break; + + case KS_INPUT_SVIDEO_1: + case KS_INPUT_SVIDEO_2: + case KS_INPUT_SVIDEO_3: + v4l2_dbg(1, debug, sd, + "s_routing %d: S-Video\n", input); + /* autodetect 50/60 Hz */ + ks0127_and_or(sd, KS_CMDA, 0xfc, 0x00); + /* VSE=0 */ + ks0127_and_or(sd, KS_CMDA, ~0x40, 0x00); + /* set input line */ + ks0127_and_or(sd, KS_CMDB, 0xb0, input); + /* non-freerunning mode */ + ks0127_and_or(sd, KS_CMDC, 0x70, 0x0a); + /* analog input */ + ks0127_and_or(sd, KS_CMDD, 0x03, 0x00); + /* enable chroma demodulation */ + ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x00); + ks0127_and_or(sd, KS_LUMA, 0x00, + reg_defaults[KS_LUMA]); + /* disable luma comb */ + ks0127_and_or(sd, KS_VERTIA, 0x08, + (reg_defaults[KS_VERTIA]&0xf0)|0x01); + ks0127_and_or(sd, KS_VERTIC, 0x0f, + reg_defaults[KS_VERTIC]&0xf0); + + ks0127_and_or(sd, KS_CHROMB, 0x0f, + reg_defaults[KS_CHROMB]&0xf0); + + ks0127_write(sd, KS_UGAIN, reg_defaults[KS_UGAIN]); + ks0127_write(sd, KS_VGAIN, reg_defaults[KS_VGAIN]); + ks0127_write(sd, KS_UVOFFH, reg_defaults[KS_UVOFFH]); + ks0127_write(sd, KS_UVOFFL, reg_defaults[KS_UVOFFL]); + break; + + case KS_INPUT_YUV656: + v4l2_dbg(1, debug, sd, "s_routing 15: YUV656\n"); + if (ks->norm & V4L2_STD_525_60) + /* force 60 Hz */ + ks0127_and_or(sd, KS_CMDA, 0xfc, 0x03); + else + /* force 50 Hz */ + ks0127_and_or(sd, KS_CMDA, 0xfc, 0x02); + + ks0127_and_or(sd, KS_CMDA, 0xff, 0x40); /* VSE=1 */ + /* set input line and VALIGN */ + ks0127_and_or(sd, KS_CMDB, 0xb0, (input | 0x40)); + /* freerunning mode, */ + /* TSTGEN = 1 TSTGFR=11 TSTGPH=0 TSTGPK=0 VMEM=1*/ + ks0127_and_or(sd, KS_CMDC, 0x70, 0x87); + /* digital input, SYNDIR = 0 INPSL=01 CLKDIR=0 EAV=0 */ + ks0127_and_or(sd, KS_CMDD, 0x03, 0x08); + /* disable chroma demodulation */ + ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x30); + /* HYPK =01 CTRAP = 0 HYBWR=0 PED=1 RGBH=1 UNIT=1 */ + ks0127_and_or(sd, KS_LUMA, 0x00, 0x71); + ks0127_and_or(sd, KS_VERTIC, 0x0f, + reg_defaults[KS_VERTIC]&0xf0); + + /* scaler fullbw, luma comb off */ + ks0127_and_or(sd, KS_VERTIA, 0x08, 0x81); + + ks0127_and_or(sd, KS_CHROMB, 0x0f, + reg_defaults[KS_CHROMB]&0xf0); + + ks0127_and_or(sd, KS_CON, 0x00, 0x00); + ks0127_and_or(sd, KS_BRT, 0x00, 32); /* spec: 34 */ + /* spec: 229 (e5) */ + ks0127_and_or(sd, KS_SAT, 0x00, 0xe8); + ks0127_and_or(sd, KS_HUE, 0x00, 0); + + ks0127_and_or(sd, KS_UGAIN, 0x00, 238); + ks0127_and_or(sd, KS_VGAIN, 0x00, 0x00); + + /*UOFF:0x30, VOFF:0x30, TSTCGN=1 */ + ks0127_and_or(sd, KS_UVOFFH, 0x00, 0x4f); + ks0127_and_or(sd, KS_UVOFFL, 0x00, 0x00); + break; + + default: + v4l2_dbg(1, debug, sd, + "s_routing: Unknown input %d\n", input); + break; + } + + /* hack: CDMLPF sometimes spontaneously switches on; */ + /* force back off */ + ks0127_write(sd, KS_DEMOD, reg_defaults[KS_DEMOD]); + return 0; +} + +static int ks0127_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct ks0127 *ks = to_ks0127(sd); + + /* Set to automatic SECAM/Fsc mode */ + ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x00); + + ks->norm = std; + if (std & V4L2_STD_NTSC) { + v4l2_dbg(1, debug, sd, + "s_std: NTSC_M\n"); + ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x20); + } else if (std & V4L2_STD_PAL_N) { + v4l2_dbg(1, debug, sd, + "s_std: NTSC_N (fixme)\n"); + ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x40); + } else if (std & V4L2_STD_PAL) { + v4l2_dbg(1, debug, sd, + "s_std: PAL_N\n"); + ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x20); + } else if (std & V4L2_STD_PAL_M) { + v4l2_dbg(1, debug, sd, + "s_std: PAL_M (fixme)\n"); + ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x40); + } else if (std & V4L2_STD_SECAM) { + v4l2_dbg(1, debug, sd, + "s_std: SECAM\n"); + + /* set to secam autodetection */ + ks0127_and_or(sd, KS_CHROMA, 0xdf, 0x20); + ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x00); + schedule_timeout_interruptible(HZ/10+1); + + /* did it autodetect? */ + if (!(ks0127_read(sd, KS_DEMOD) & 0x40)) + /* force to secam mode */ + ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x0f); + } else { + v4l2_dbg(1, debug, sd, "s_std: Unknown norm %llx\n", + (unsigned long long)std); + } + return 0; +} + +static int ks0127_s_stream(struct v4l2_subdev *sd, int enable) +{ + v4l2_dbg(1, debug, sd, "s_stream(%d)\n", enable); + if (enable) { + /* All output pins on */ + ks0127_and_or(sd, KS_OFMTA, 0xcf, 0x30); + /* Obey the OEN pin */ + ks0127_and_or(sd, KS_CDEM, 0x7f, 0x00); + } else { + /* Video output pins off */ + ks0127_and_or(sd, KS_OFMTA, 0xcf, 0x00); + /* Ignore the OEN pin */ + ks0127_and_or(sd, KS_CDEM, 0x7f, 0x80); + } + return 0; +} + +static int ks0127_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd) +{ + int stat = V4L2_IN_ST_NO_SIGNAL; + u8 status; + v4l2_std_id std = V4L2_STD_ALL; + + status = ks0127_read(sd, KS_STAT); + if (!(status & 0x20)) /* NOVID not set */ + stat = 0; + if (!(status & 0x01)) /* CLOCK set */ + stat |= V4L2_IN_ST_NO_COLOR; + if ((status & 0x08)) /* PALDET set */ + std = V4L2_STD_PAL; + else + std = V4L2_STD_NTSC; + if (pstd) + *pstd = std; + if (pstatus) + *pstatus = stat; + return 0; +} + +static int ks0127_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + v4l2_dbg(1, debug, sd, "querystd\n"); + return ks0127_status(sd, NULL, std); +} + +static int ks0127_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + v4l2_dbg(1, debug, sd, "g_input_status\n"); + return ks0127_status(sd, status, NULL); +} + +static int ks0127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ks0127 *ks = to_ks0127(sd); + + return v4l2_chip_ident_i2c_client(client, chip, ks->ident, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops ks0127_core_ops = { + .g_chip_ident = ks0127_g_chip_ident, + .s_std = ks0127_s_std, +}; + +static const struct v4l2_subdev_video_ops ks0127_video_ops = { + .s_routing = ks0127_s_routing, + .s_stream = ks0127_s_stream, + .querystd = ks0127_querystd, + .g_input_status = ks0127_g_input_status, +}; + +static const struct v4l2_subdev_ops ks0127_ops = { + .core = &ks0127_core_ops, + .video = &ks0127_video_ops, +}; + +/* ----------------------------------------------------------------------- */ + + +static int ks0127_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct ks0127 *ks; + struct v4l2_subdev *sd; + + v4l_info(client, "%s chip found @ 0x%x (%s)\n", + client->addr == (I2C_KS0127_ADDON >> 1) ? "addon" : "on-board", + client->addr << 1, client->adapter->name); + + ks = kzalloc(sizeof(*ks), GFP_KERNEL); + if (ks == NULL) + return -ENOMEM; + sd = &ks->sd; + v4l2_i2c_subdev_init(sd, client, &ks0127_ops); + + /* power up */ + init_reg_defaults(); + ks0127_write(sd, KS_CMDA, 0x2c); + mdelay(10); + + /* reset the device */ + ks0127_init(sd); + return 0; +} + +static int ks0127_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + ks0127_write(sd, KS_OFMTA, 0x20); /* tristate */ + ks0127_write(sd, KS_CMDA, 0x2c | 0x80); /* power down */ + kfree(to_ks0127(sd)); + return 0; +} + +static const struct i2c_device_id ks0127_id[] = { + { "ks0127", 0 }, + { "ks0127b", 0 }, + { "ks0122s", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ks0127_id); + +static struct i2c_driver ks0127_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "ks0127", + }, + .probe = ks0127_probe, + .remove = ks0127_remove, + .id_table = ks0127_id, +}; + +module_i2c_driver(ks0127_driver); diff --git a/drivers/media/i2c/ks0127.h b/drivers/media/i2c/ks0127.h new file mode 100644 index 00000000000..cb8abd5403b --- /dev/null +++ b/drivers/media/i2c/ks0127.h @@ -0,0 +1,51 @@ +/* + * Video Capture Driver ( Video for Linux 1/2 ) + * for the Matrox Marvel G200,G400 and Rainbow Runner-G series + * + * This module is an interface to the KS0127 video decoder chip. + * + * Copyright (C) 1999 Ryan Drake + * + * 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. + */ + +#ifndef KS0127_H +#define KS0127_H + +/* input channels */ +#define KS_INPUT_COMPOSITE_1 0 +#define KS_INPUT_COMPOSITE_2 1 +#define KS_INPUT_COMPOSITE_3 2 +#define KS_INPUT_COMPOSITE_4 4 +#define KS_INPUT_COMPOSITE_5 5 +#define KS_INPUT_COMPOSITE_6 6 + +#define KS_INPUT_SVIDEO_1 8 +#define KS_INPUT_SVIDEO_2 9 +#define KS_INPUT_SVIDEO_3 10 + +#define KS_INPUT_YUV656 15 +#define KS_INPUT_COUNT 10 + +/* output channels */ +#define KS_OUTPUT_YUV656E 0 +#define KS_OUTPUT_EXV 1 + +/* video standards */ +#define KS_STD_NTSC_N 112 /* 50 Hz NTSC */ +#define KS_STD_PAL_M 113 /* 60 Hz PAL */ + +#endif /* KS0127_H */ + diff --git a/drivers/media/i2c/m52790.c b/drivers/media/i2c/m52790.c new file mode 100644 index 00000000000..0991576f4c8 --- /dev/null +++ b/drivers/media/i2c/m52790.c @@ -0,0 +1,216 @@ +/* + * m52790 i2c ivtv driver. + * Copyright (C) 2007 Hans Verkuil + * + * A/V source switching Mitsubishi M52790SP/FP + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("i2c device driver for m52790 A/V switch"); +MODULE_AUTHOR("Hans Verkuil"); +MODULE_LICENSE("GPL"); + + +struct m52790_state { + struct v4l2_subdev sd; + u16 input; + u16 output; +}; + +static inline struct m52790_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct m52790_state, sd); +} + +/* ----------------------------------------------------------------------- */ + +static int m52790_write(struct v4l2_subdev *sd) +{ + struct m52790_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + u8 sw1 = (state->input | state->output) & 0xff; + u8 sw2 = (state->input | state->output) >> 8; + + return i2c_smbus_write_byte_data(client, sw1, sw2); +} + +/* Note: audio and video are linked and cannot be switched separately. + So audio and video routing commands are identical for this chip. + In theory the video amplifier and audio modes could be handled + separately for the output, but that seems to be overkill right now. + The same holds for implementing an audio mute control, this is now + part of the audio output routing. The normal case is that another + chip takes care of the actual muting so making it part of the + output routing seems to be the right thing to do for now. */ +static int m52790_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct m52790_state *state = to_state(sd); + + state->input = input; + state->output = output; + m52790_write(sd); + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int m52790_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct m52790_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (reg->reg != 0) + return -EINVAL; + reg->size = 1; + reg->val = state->input | state->output; + return 0; +} + +static int m52790_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct m52790_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (reg->reg != 0) + return -EINVAL; + state->input = reg->val & 0x0303; + state->output = reg->val & ~0x0303; + m52790_write(sd); + return 0; +} +#endif + +static int m52790_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_M52790, 0); +} + +static int m52790_log_status(struct v4l2_subdev *sd) +{ + struct m52790_state *state = to_state(sd); + + v4l2_info(sd, "Switch 1: %02x\n", + (state->input | state->output) & 0xff); + v4l2_info(sd, "Switch 2: %02x\n", + (state->input | state->output) >> 8); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops m52790_core_ops = { + .log_status = m52790_log_status, + .g_chip_ident = m52790_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = m52790_g_register, + .s_register = m52790_s_register, +#endif +}; + +static const struct v4l2_subdev_audio_ops m52790_audio_ops = { + .s_routing = m52790_s_routing, +}; + +static const struct v4l2_subdev_video_ops m52790_video_ops = { + .s_routing = m52790_s_routing, +}; + +static const struct v4l2_subdev_ops m52790_ops = { + .core = &m52790_core_ops, + .audio = &m52790_audio_ops, + .video = &m52790_video_ops, +}; + +/* ----------------------------------------------------------------------- */ + +/* i2c implementation */ + +static int m52790_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct m52790_state *state; + struct v4l2_subdev *sd; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct m52790_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &m52790_ops); + state->input = M52790_IN_TUNER; + state->output = M52790_OUT_STEREO; + m52790_write(sd); + return 0; +} + +static int m52790_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 m52790_id[] = { + { "m52790", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, m52790_id); + +static struct i2c_driver m52790_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "m52790", + }, + .probe = m52790_probe, + .remove = m52790_remove, + .id_table = m52790_id, +}; + +module_i2c_driver(m52790_driver); diff --git a/drivers/media/i2c/m5mols/Kconfig b/drivers/media/i2c/m5mols/Kconfig new file mode 100644 index 00000000000..dc8c2505907 --- /dev/null +++ b/drivers/media/i2c/m5mols/Kconfig @@ -0,0 +1,6 @@ +config VIDEO_M5MOLS + tristate "Fujitsu M-5MOLS 8MP sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This driver supports Fujitsu M-5MOLS camera sensor with ISP diff --git a/drivers/media/i2c/m5mols/Makefile b/drivers/media/i2c/m5mols/Makefile new file mode 100644 index 00000000000..0a44e028edc --- /dev/null +++ b/drivers/media/i2c/m5mols/Makefile @@ -0,0 +1,3 @@ +m5mols-objs := m5mols_core.o m5mols_controls.o m5mols_capture.o + +obj-$(CONFIG_VIDEO_M5MOLS) += m5mols.o diff --git a/drivers/media/i2c/m5mols/m5mols.h b/drivers/media/i2c/m5mols/m5mols.h new file mode 100644 index 00000000000..bb589917b65 --- /dev/null +++ b/drivers/media/i2c/m5mols/m5mols.h @@ -0,0 +1,334 @@ +/* + * Header for M-5MOLS 8M Pixel camera sensor with ISP + * + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * Author: HeungJun Kim + * + * Copyright (C) 2009 Samsung Electronics Co., Ltd. + * Author: Dongsoo Nathaniel Kim + * + * 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. + */ + +#ifndef M5MOLS_H +#define M5MOLS_H + +#include +#include "m5mols_reg.h" + +extern int m5mols_debug; + +enum m5mols_restype { + M5MOLS_RESTYPE_MONITOR, + M5MOLS_RESTYPE_CAPTURE, + M5MOLS_RESTYPE_MAX, +}; + +/** + * struct m5mols_resolution - structure for the resolution + * @type: resolution type according to the pixel code + * @width: width of the resolution + * @height: height of the resolution + * @reg: resolution preset register value + */ +struct m5mols_resolution { + u8 reg; + enum m5mols_restype type; + u16 width; + u16 height; +}; + +/** + * struct m5mols_exif - structure for the EXIF information of M-5MOLS + * @exposure_time: exposure time register value + * @shutter_speed: speed of the shutter register value + * @aperture: aperture register value + * @exposure_bias: it calls also EV bias + * @iso_speed: ISO register value + * @flash: status register value of the flash + * @sdr: status register value of the Subject Distance Range + * @qval: not written exact meaning in document + */ +struct m5mols_exif { + u32 exposure_time; + u32 shutter_speed; + u32 aperture; + u32 brightness; + u32 exposure_bias; + u16 iso_speed; + u16 flash; + u16 sdr; + u16 qval; +}; + +/** + * struct m5mols_capture - Structure for the capture capability + * @exif: EXIF information + * @main: size in bytes of the main image + * @thumb: size in bytes of the thumb image, if it was accompanied + * @total: total size in bytes of the produced image + */ +struct m5mols_capture { + struct m5mols_exif exif; + u32 main; + u32 thumb; + u32 total; +}; + +/** + * struct m5mols_scenemode - structure for the scenemode capability + * @metering: metering light register value + * @ev_bias: EV bias register value + * @wb_mode: mode which means the WhiteBalance is Auto or Manual + * @wb_preset: whitebalance preset register value in the Manual mode + * @chroma_en: register value whether the Chroma capability is enabled or not + * @chroma_lvl: chroma's level register value + * @edge_en: register value Whether the Edge capability is enabled or not + * @edge_lvl: edge's level register value + * @af_range: Auto Focus's range + * @fd_mode: Face Detection mode + * @mcc: Multi-axis Color Conversion which means emotion color + * @light: status of the Light + * @flash: status of the Flash + * @tone: Tone color which means Contrast + * @iso: ISO register value + * @capt_mode: Mode of the Image Stabilization while the camera capturing + * @wdr: Wide Dynamic Range register value + * + * The each value according to each scenemode is recommended in the documents. + */ +struct m5mols_scenemode { + u8 metering; + u8 ev_bias; + u8 wb_mode; + u8 wb_preset; + u8 chroma_en; + u8 chroma_lvl; + u8 edge_en; + u8 edge_lvl; + u8 af_range; + u8 fd_mode; + u8 mcc; + u8 light; + u8 flash; + u8 tone; + u8 iso; + u8 capt_mode; + u8 wdr; +}; + +/** + * struct m5mols_version - firmware version information + * @customer: customer information + * @project: version of project information according to customer + * @fw: firmware revision + * @hw: hardware revision + * @param: version of the parameter + * @awb: Auto WhiteBalance algorithm version + * @str: information about manufacturer and packaging vendor + * @af: Auto Focus version + * + * The register offset starts the customer version at 0x0, and it ends + * the awb version at 0x09. The customer, project information occupies 1 bytes + * each. And also the fw, hw, param, awb each requires 2 bytes. The str is + * unique string associated with firmware's version. It includes information + * about manufacturer and the vendor of the sensor's packaging. The least + * significant 2 bytes of the string indicate packaging manufacturer. + */ +#define VERSION_STRING_SIZE 22 +struct m5mols_version { + u8 customer; + u8 project; + u16 fw; + u16 hw; + u16 param; + u16 awb; + u8 str[VERSION_STRING_SIZE]; + u8 af; +}; + +/** + * struct m5mols_info - M-5MOLS driver data structure + * @pdata: platform data + * @sd: v4l-subdev instance + * @pad: media pad + * @ffmt: current fmt according to resolution type + * @res_type: current resolution type + * @irq_waitq: waitqueue for the capture + * @irq_done: set to 1 in the interrupt handler + * @handle: control handler + * @auto_exposure: auto/manual exposure control + * @exposure_bias: exposure compensation control + * @exposure: manual exposure control + * @metering: exposure metering control + * @auto_iso: auto/manual ISO sensitivity control + * @iso: manual ISO sensitivity control + * @auto_wb: auto white balance control + * @lock_3a: 3A lock control + * @colorfx: color effect control + * @saturation: saturation control + * @zoom: zoom control + * @wdr: wide dynamic range control + * @stabilization: image stabilization control + * @jpeg_quality: JPEG compression quality control + * @ver: information of the version + * @cap: the capture mode attributes + * @isp_ready: 1 when the ISP controller has completed booting + * @power: current sensor's power status + * @ctrl_sync: 1 when the control handler state is restored in H/W + * @resolution: register value for current resolution + * @mode: register value for current operation mode + * @set_power: optional power callback to the board code + */ +struct m5mols_info { + const struct m5mols_platform_data *pdata; + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_mbus_framefmt ffmt[M5MOLS_RESTYPE_MAX]; + int res_type; + + wait_queue_head_t irq_waitq; + atomic_t irq_done; + + struct v4l2_ctrl_handler handle; + struct { + /* exposure/exposure bias/auto exposure cluster */ + struct v4l2_ctrl *auto_exposure; + struct v4l2_ctrl *exposure_bias; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *metering; + }; + struct { + /* iso/auto iso cluster */ + struct v4l2_ctrl *auto_iso; + struct v4l2_ctrl *iso; + }; + struct v4l2_ctrl *auto_wb; + + struct v4l2_ctrl *lock_3a; + struct v4l2_ctrl *colorfx; + struct v4l2_ctrl *saturation; + struct v4l2_ctrl *zoom; + struct v4l2_ctrl *wdr; + struct v4l2_ctrl *stabilization; + struct v4l2_ctrl *jpeg_quality; + + struct m5mols_version ver; + struct m5mols_capture cap; + + unsigned int isp_ready:1; + unsigned int power:1; + unsigned int ctrl_sync:1; + + u8 resolution; + u8 mode; + + int (*set_power)(struct device *dev, int on); +}; + +#define is_available_af(__info) (__info->ver.af) +#define is_code(__code, __type) (__code == m5mols_default_ffmt[__type].code) +#define is_manufacturer(__info, __manufacturer) \ + (__info->ver.str[0] == __manufacturer[0] && \ + __info->ver.str[1] == __manufacturer[1]) +/* + * I2C operation of the M-5MOLS + * + * The I2C read operation of the M-5MOLS requires 2 messages. The first + * message sends the information about the command, command category, and total + * message size. The second message is used to retrieve the data specifed in + * the first message + * + * 1st message 2nd message + * +-------+---+----------+-----+-------+ +------+------+------+------+ + * | size1 | R | category | cmd | size2 | | d[0] | d[1] | d[2] | d[3] | + * +-------+---+----------+-----+-------+ +------+------+------+------+ + * - size1: message data size(5 in this case) + * - size2: desired buffer size of the 2nd message + * - d[0..3]: according to size2 + * + * The I2C write operation needs just one message. The message includes + * category, command, total size, and desired data. + * + * 1st message + * +-------+---+----------+-----+------+------+------+------+ + * | size1 | W | category | cmd | d[0] | d[1] | d[2] | d[3] | + * +-------+---+----------+-----+------+------+------+------+ + * - d[0..3]: according to size1 + */ +int m5mols_read_u8(struct v4l2_subdev *sd, u32 reg_comb, u8 *val); +int m5mols_read_u16(struct v4l2_subdev *sd, u32 reg_comb, u16 *val); +int m5mols_read_u32(struct v4l2_subdev *sd, u32 reg_comb, u32 *val); +int m5mols_write(struct v4l2_subdev *sd, u32 reg_comb, u32 val); + +int m5mols_busy_wait(struct v4l2_subdev *sd, u32 reg, u32 value, u32 mask, + int timeout); + +/* Mask value for busy waiting until M-5MOLS I2C interface is initialized */ +#define M5MOLS_I2C_RDY_WAIT_FL (1 << 16) +/* ISP state transition timeout, in ms */ +#define M5MOLS_MODE_CHANGE_TIMEOUT 200 +#define M5MOLS_BUSY_WAIT_DEF_TIMEOUT 250 + +/* + * Mode operation of the M-5MOLS + * + * Changing the mode of the M-5MOLS is needed right executing order. + * There are three modes(PARAMETER, MONITOR, CAPTURE) which can be changed + * by user. There are various categories associated with each mode. + * + * +============================================================+ + * | mode | category | + * +============================================================+ + * | FLASH | FLASH(only after Stand-by or Power-on) | + * | SYSTEM | SYSTEM(only after sensor arm-booting) | + * | PARAMETER | PARAMETER | + * | MONITOR | MONITOR(preview), Auto Focus, Face Detection | + * | CAPTURE | Single CAPTURE, Preview(recording) | + * +============================================================+ + * + * The available executing order between each modes are as follows: + * PARAMETER <---> MONITOR <---> CAPTURE + */ +int m5mols_set_mode(struct m5mols_info *info, u8 mode); + +int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg); +int m5mols_wait_interrupt(struct v4l2_subdev *sd, u8 condition, u32 timeout); +int m5mols_restore_controls(struct m5mols_info *info); +int m5mols_start_capture(struct m5mols_info *info); +int m5mols_do_scenemode(struct m5mols_info *info, u8 mode); +int m5mols_lock_3a(struct m5mols_info *info, bool lock); +int m5mols_set_ctrl(struct v4l2_ctrl *ctrl); +int m5mols_init_controls(struct v4l2_subdev *sd); + +/* The firmware function */ +int m5mols_update_fw(struct v4l2_subdev *sd, + int (*set_power)(struct m5mols_info *, bool)); + +static inline struct m5mols_info *to_m5mols(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct m5mols_info, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + struct m5mols_info *info = container_of(ctrl->handler, + struct m5mols_info, handle); + return &info->sd; +} + +static inline void m5mols_set_ctrl_mode(struct v4l2_ctrl *ctrl, + unsigned int mode) +{ + ctrl->priv = (void *)mode; +} + +static inline unsigned int m5mols_get_ctrl_mode(struct v4l2_ctrl *ctrl) +{ + return (unsigned int)ctrl->priv; +} + +#endif /* M5MOLS_H */ diff --git a/drivers/media/i2c/m5mols/m5mols_capture.c b/drivers/media/i2c/m5mols/m5mols_capture.c new file mode 100644 index 00000000000..cb243bd278c --- /dev/null +++ b/drivers/media/i2c/m5mols/m5mols_capture.c @@ -0,0 +1,155 @@ + +/* + * The Capture code for Fujitsu M-5MOLS ISP + * + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * Author: HeungJun Kim + * + * Copyright (C) 2009 Samsung Electronics Co., Ltd. + * Author: Dongsoo Nathaniel Kim + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "m5mols.h" +#include "m5mols_reg.h" + +/** + * m5mols_read_rational - I2C read of a rational number + * + * Read numerator and denominator from registers @addr_num and @addr_den + * respectively and return the division result in @val. + */ +static int m5mols_read_rational(struct v4l2_subdev *sd, u32 addr_num, + u32 addr_den, u32 *val) +{ + u32 num, den; + + int ret = m5mols_read_u32(sd, addr_num, &num); + if (!ret) + ret = m5mols_read_u32(sd, addr_den, &den); + if (ret) + return ret; + *val = den == 0 ? 0 : num / den; + return ret; +} + +/** + * m5mols_capture_info - Gather captured image information + * + * For now it gathers only EXIF information and file size. + */ +static int m5mols_capture_info(struct m5mols_info *info) +{ + struct m5mols_exif *exif = &info->cap.exif; + struct v4l2_subdev *sd = &info->sd; + int ret; + + ret = m5mols_read_rational(sd, EXIF_INFO_EXPTIME_NU, + EXIF_INFO_EXPTIME_DE, &exif->exposure_time); + if (ret) + return ret; + ret = m5mols_read_rational(sd, EXIF_INFO_TV_NU, EXIF_INFO_TV_DE, + &exif->shutter_speed); + if (ret) + return ret; + ret = m5mols_read_rational(sd, EXIF_INFO_AV_NU, EXIF_INFO_AV_DE, + &exif->aperture); + if (ret) + return ret; + ret = m5mols_read_rational(sd, EXIF_INFO_BV_NU, EXIF_INFO_BV_DE, + &exif->brightness); + if (ret) + return ret; + ret = m5mols_read_rational(sd, EXIF_INFO_EBV_NU, EXIF_INFO_EBV_DE, + &exif->exposure_bias); + if (ret) + return ret; + + ret = m5mols_read_u16(sd, EXIF_INFO_ISO, &exif->iso_speed); + if (!ret) + ret = m5mols_read_u16(sd, EXIF_INFO_FLASH, &exif->flash); + if (!ret) + ret = m5mols_read_u16(sd, EXIF_INFO_SDR, &exif->sdr); + if (!ret) + ret = m5mols_read_u16(sd, EXIF_INFO_QVAL, &exif->qval); + if (ret) + return ret; + + if (!ret) + ret = m5mols_read_u32(sd, CAPC_IMAGE_SIZE, &info->cap.main); + if (!ret) + ret = m5mols_read_u32(sd, CAPC_THUMB_SIZE, &info->cap.thumb); + if (!ret) + info->cap.total = info->cap.main + info->cap.thumb; + + return ret; +} + +int m5mols_start_capture(struct m5mols_info *info) +{ + struct v4l2_subdev *sd = &info->sd; + int ret; + + /* + * Synchronize the controls, set the capture frame resolution and color + * format. The frame capture is initiated during switching from Monitor + * to Capture mode. + */ + ret = m5mols_set_mode(info, REG_MONITOR); + if (!ret) + ret = m5mols_restore_controls(info); + if (!ret) + ret = m5mols_write(sd, CAPP_YUVOUT_MAIN, REG_JPEG); + if (!ret) + ret = m5mols_write(sd, CAPP_MAIN_IMAGE_SIZE, info->resolution); + if (!ret) + ret = m5mols_set_mode(info, REG_CAPTURE); + if (!ret) + /* Wait until a frame is captured to ISP internal memory */ + ret = m5mols_wait_interrupt(sd, REG_INT_CAPTURE, 2000); + if (ret) + return ret; + + /* + * Initiate the captured data transfer to a MIPI-CSI receiver. + */ + ret = m5mols_write(sd, CAPC_SEL_FRAME, 1); + if (!ret) + ret = m5mols_write(sd, CAPC_START, REG_CAP_START_MAIN); + if (!ret) { + bool captured = false; + unsigned int size; + + /* Wait for the capture completion interrupt */ + ret = m5mols_wait_interrupt(sd, REG_INT_CAPTURE, 2000); + if (!ret) { + captured = true; + ret = m5mols_capture_info(info); + } + size = captured ? info->cap.main : 0; + v4l2_dbg(1, m5mols_debug, sd, "%s: size: %d, thumb.: %d B\n", + __func__, size, info->cap.thumb); + + v4l2_subdev_notify(sd, S5P_FIMC_TX_END_NOTIFY, &size); + } + + return ret; +} diff --git a/drivers/media/i2c/m5mols/m5mols_controls.c b/drivers/media/i2c/m5mols/m5mols_controls.c new file mode 100644 index 00000000000..fdbc205a296 --- /dev/null +++ b/drivers/media/i2c/m5mols/m5mols_controls.c @@ -0,0 +1,628 @@ +/* + * Controls for M-5MOLS 8M Pixel camera sensor with ISP + * + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * Author: HeungJun Kim + * + * Copyright (C) 2009 Samsung Electronics Co., Ltd. + * Author: Dongsoo Nathaniel Kim + * + * 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. + */ + +#include +#include +#include +#include + +#include "m5mols.h" +#include "m5mols_reg.h" + +static struct m5mols_scenemode m5mols_default_scenemode[] = { + [REG_SCENE_NORMAL] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_NORMAL, REG_LIGHT_OFF, REG_FLASH_OFF, + 5, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_PORTRAIT] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 4, + REG_AF_NORMAL, BIT_FD_EN | BIT_FD_DRAW_FACE_FRAME, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_LANDSCAPE] = { + REG_AE_ALL, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 4, REG_EDGE_ON, 6, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_SPORTS] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_PARTY_INDOOR] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 4, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_200, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_BEACH_SNOW] = { + REG_AE_CENTER, REG_AE_INDEX_10_POS, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 4, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_SUNSET] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET, + REG_AWB_DAYLIGHT, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_DAWN_DUSK] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET, + REG_AWB_FLUORESCENT_1, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_FALL] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 5, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_NIGHT] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_AGAINST_LIGHT] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_FIRE] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_TEXT] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 7, + REG_AF_MACRO, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_ANTI_SHAKE, REG_WDR_ON, + }, + [REG_SCENE_CANDLE] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, +}; + +/** + * m5mols_do_scenemode() - Change current scenemode + * @mode: Desired mode of the scenemode + * + * WARNING: The execution order is important. Do not change the order. + */ +int m5mols_do_scenemode(struct m5mols_info *info, u8 mode) +{ + struct v4l2_subdev *sd = &info->sd; + struct m5mols_scenemode scenemode = m5mols_default_scenemode[mode]; + int ret; + + if (mode > REG_SCENE_CANDLE) + return -EINVAL; + + ret = v4l2_ctrl_s_ctrl(info->lock_3a, 0); + if (!ret) + ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, mode); + if (!ret) + ret = m5mols_write(sd, AE_EV_PRESET_CAPTURE, mode); + if (!ret) + ret = m5mols_write(sd, AE_MODE, scenemode.metering); + if (!ret) + ret = m5mols_write(sd, AE_INDEX, scenemode.ev_bias); + if (!ret) + ret = m5mols_write(sd, AWB_MODE, scenemode.wb_mode); + if (!ret) + ret = m5mols_write(sd, AWB_MANUAL, scenemode.wb_preset); + if (!ret) + ret = m5mols_write(sd, MON_CHROMA_EN, scenemode.chroma_en); + if (!ret) + ret = m5mols_write(sd, MON_CHROMA_LVL, scenemode.chroma_lvl); + if (!ret) + ret = m5mols_write(sd, MON_EDGE_EN, scenemode.edge_en); + if (!ret) + ret = m5mols_write(sd, MON_EDGE_LVL, scenemode.edge_lvl); + if (!ret && is_available_af(info)) + ret = m5mols_write(sd, AF_MODE, scenemode.af_range); + if (!ret && is_available_af(info)) + ret = m5mols_write(sd, FD_CTL, scenemode.fd_mode); + if (!ret) + ret = m5mols_write(sd, MON_TONE_CTL, scenemode.tone); + if (!ret) + ret = m5mols_write(sd, AE_ISO, scenemode.iso); + if (!ret) + ret = m5mols_set_mode(info, REG_CAPTURE); + if (!ret) + ret = m5mols_write(sd, CAPP_WDR_EN, scenemode.wdr); + if (!ret) + ret = m5mols_write(sd, CAPP_MCC_MODE, scenemode.mcc); + if (!ret) + ret = m5mols_write(sd, CAPP_LIGHT_CTRL, scenemode.light); + if (!ret) + ret = m5mols_write(sd, CAPP_FLASH_CTRL, scenemode.flash); + if (!ret) + ret = m5mols_write(sd, CAPC_MODE, scenemode.capt_mode); + if (!ret) + ret = m5mols_set_mode(info, REG_MONITOR); + + return ret; +} + +static int m5mols_3a_lock(struct m5mols_info *info, struct v4l2_ctrl *ctrl) +{ + bool af_lock = ctrl->val & V4L2_LOCK_FOCUS; + int ret = 0; + + if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_EXPOSURE) { + bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE; + + ret = m5mols_write(&info->sd, AE_LOCK, ae_lock ? + REG_AE_LOCK : REG_AE_UNLOCK); + if (ret) + return ret; + } + + if (((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_WHITE_BALANCE) + && info->auto_wb->val) { + bool awb_lock = ctrl->val & V4L2_LOCK_WHITE_BALANCE; + + ret = m5mols_write(&info->sd, AWB_LOCK, awb_lock ? + REG_AWB_LOCK : REG_AWB_UNLOCK); + if (ret) + return ret; + } + + if (!info->ver.af || !af_lock) + return ret; + + if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_FOCUS) + ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP); + + return ret; +} + +static int m5mols_set_metering_mode(struct m5mols_info *info, int mode) +{ + unsigned int metering; + + switch (mode) { + case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: + metering = REG_AE_CENTER; + break; + case V4L2_EXPOSURE_METERING_SPOT: + metering = REG_AE_SPOT; + break; + default: + metering = REG_AE_ALL; + break; + } + + return m5mols_write(&info->sd, AE_MODE, metering); +} + +static int m5mols_set_exposure(struct m5mols_info *info, int exposure) +{ + struct v4l2_subdev *sd = &info->sd; + int ret = 0; + + if (exposure == V4L2_EXPOSURE_AUTO) { + /* Unlock auto exposure */ + info->lock_3a->val &= ~V4L2_LOCK_EXPOSURE; + m5mols_3a_lock(info, info->lock_3a); + + ret = m5mols_set_metering_mode(info, info->metering->val); + if (ret < 0) + return ret; + + v4l2_dbg(1, m5mols_debug, sd, + "%s: exposure bias: %#x, metering: %#x\n", + __func__, info->exposure_bias->val, + info->metering->val); + + return m5mols_write(sd, AE_INDEX, info->exposure_bias->val); + } + + if (exposure == V4L2_EXPOSURE_MANUAL) { + ret = m5mols_write(sd, AE_MODE, REG_AE_OFF); + if (ret == 0) + ret = m5mols_write(sd, AE_MAN_GAIN_MON, + info->exposure->val); + if (ret == 0) + ret = m5mols_write(sd, AE_MAN_GAIN_CAP, + info->exposure->val); + + v4l2_dbg(1, m5mols_debug, sd, "%s: exposure: %#x\n", + __func__, info->exposure->val); + } + + return ret; +} + +static int m5mols_set_white_balance(struct m5mols_info *info, int val) +{ + static const unsigned short wb[][2] = { + { V4L2_WHITE_BALANCE_INCANDESCENT, REG_AWB_INCANDESCENT }, + { V4L2_WHITE_BALANCE_FLUORESCENT, REG_AWB_FLUORESCENT_1 }, + { V4L2_WHITE_BALANCE_FLUORESCENT_H, REG_AWB_FLUORESCENT_2 }, + { V4L2_WHITE_BALANCE_HORIZON, REG_AWB_HORIZON }, + { V4L2_WHITE_BALANCE_DAYLIGHT, REG_AWB_DAYLIGHT }, + { V4L2_WHITE_BALANCE_FLASH, REG_AWB_LEDLIGHT }, + { V4L2_WHITE_BALANCE_CLOUDY, REG_AWB_CLOUDY }, + { V4L2_WHITE_BALANCE_SHADE, REG_AWB_SHADE }, + { V4L2_WHITE_BALANCE_AUTO, REG_AWB_AUTO }, + }; + int i; + struct v4l2_subdev *sd = &info->sd; + int ret = -EINVAL; + + for (i = 0; i < ARRAY_SIZE(wb); i++) { + int awb; + if (wb[i][0] != val) + continue; + + v4l2_dbg(1, m5mols_debug, sd, + "Setting white balance to: %#x\n", wb[i][0]); + + awb = wb[i][0] == V4L2_WHITE_BALANCE_AUTO; + ret = m5mols_write(sd, AWB_MODE, awb ? REG_AWB_AUTO : + REG_AWB_PRESET); + if (ret < 0) + return ret; + + if (!awb) + ret = m5mols_write(sd, AWB_MANUAL, wb[i][1]); + } + + return ret; +} + +static int m5mols_set_saturation(struct m5mols_info *info, int val) +{ + int ret = m5mols_write(&info->sd, MON_CHROMA_LVL, val); + if (ret < 0) + return ret; + + return m5mols_write(&info->sd, MON_CHROMA_EN, REG_CHROMA_ON); +} + +static int m5mols_set_color_effect(struct m5mols_info *info, int val) +{ + unsigned int m_effect = REG_COLOR_EFFECT_OFF; + unsigned int p_effect = REG_EFFECT_OFF; + unsigned int cfix_r = 0, cfix_b = 0; + struct v4l2_subdev *sd = &info->sd; + int ret = 0; + + switch (val) { + case V4L2_COLORFX_BW: + m_effect = REG_COLOR_EFFECT_ON; + break; + case V4L2_COLORFX_NEGATIVE: + p_effect = REG_EFFECT_NEGA; + break; + case V4L2_COLORFX_EMBOSS: + p_effect = REG_EFFECT_EMBOSS; + break; + case V4L2_COLORFX_SEPIA: + m_effect = REG_COLOR_EFFECT_ON; + cfix_r = REG_CFIXR_SEPIA; + cfix_b = REG_CFIXB_SEPIA; + break; + } + + ret = m5mols_write(sd, PARM_EFFECT, p_effect); + if (!ret) + ret = m5mols_write(sd, MON_EFFECT, m_effect); + + if (ret == 0 && m_effect == REG_COLOR_EFFECT_ON) { + ret = m5mols_write(sd, MON_CFIXR, cfix_r); + if (!ret) + ret = m5mols_write(sd, MON_CFIXB, cfix_b); + } + + v4l2_dbg(1, m5mols_debug, sd, + "p_effect: %#x, m_effect: %#x, r: %#x, b: %#x (%d)\n", + p_effect, m_effect, cfix_r, cfix_b, ret); + + return ret; +} + +static int m5mols_set_iso(struct m5mols_info *info, int auto_iso) +{ + u32 iso = auto_iso ? 0 : info->iso->val + 1; + + return m5mols_write(&info->sd, AE_ISO, iso); +} + +static int m5mols_set_wdr(struct m5mols_info *info, int wdr) +{ + int ret; + + ret = m5mols_write(&info->sd, MON_TONE_CTL, wdr ? 9 : 5); + if (ret < 0) + return ret; + + ret = m5mols_set_mode(info, REG_CAPTURE); + if (ret < 0) + return ret; + + return m5mols_write(&info->sd, CAPP_WDR_EN, wdr); +} + +static int m5mols_set_stabilization(struct m5mols_info *info, int val) +{ + struct v4l2_subdev *sd = &info->sd; + unsigned int evp = val ? 0xe : 0x0; + int ret; + + ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, evp); + if (ret < 0) + return ret; + + return m5mols_write(sd, AE_EV_PRESET_CAPTURE, evp); +} + +static int m5mols_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct m5mols_info *info = to_m5mols(sd); + int ret = 0; + u8 status; + + v4l2_dbg(1, m5mols_debug, sd, "%s: ctrl: %s (%d)\n", + __func__, ctrl->name, info->isp_ready); + + if (!info->isp_ready) + return -EBUSY; + + switch (ctrl->id) { + case V4L2_CID_ISO_SENSITIVITY_AUTO: + ret = m5mols_read_u8(sd, AE_ISO, &status); + if (ret == 0) + ctrl->val = !status; + if (status != REG_ISO_AUTO) + info->iso->val = status - 1; + break; + + case V4L2_CID_3A_LOCK: + ctrl->val &= ~0x7; + + ret = m5mols_read_u8(sd, AE_LOCK, &status); + if (ret) + return ret; + if (status) + info->lock_3a->val |= V4L2_LOCK_EXPOSURE; + + ret = m5mols_read_u8(sd, AWB_LOCK, &status); + if (ret) + return ret; + if (status) + info->lock_3a->val |= V4L2_LOCK_EXPOSURE; + + ret = m5mols_read_u8(sd, AF_EXECUTE, &status); + if (!status) + info->lock_3a->val |= V4L2_LOCK_EXPOSURE; + break; + } + + return ret; +} + +static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) +{ + unsigned int ctrl_mode = m5mols_get_ctrl_mode(ctrl); + struct v4l2_subdev *sd = to_sd(ctrl); + struct m5mols_info *info = to_m5mols(sd); + int last_mode = info->mode; + int ret = 0; + + /* + * If needed, defer restoring the controls until + * the device is fully initialized. + */ + if (!info->isp_ready) { + info->ctrl_sync = 0; + return 0; + } + + v4l2_dbg(1, m5mols_debug, sd, "%s: %s, val: %d, priv: %#x\n", + __func__, ctrl->name, ctrl->val, (int)ctrl->priv); + + if (ctrl_mode && ctrl_mode != info->mode) { + ret = m5mols_set_mode(info, ctrl_mode); + if (ret < 0) + return ret; + } + + switch (ctrl->id) { + case V4L2_CID_3A_LOCK: + ret = m5mols_3a_lock(info, ctrl); + break; + + case V4L2_CID_ZOOM_ABSOLUTE: + ret = m5mols_write(sd, MON_ZOOM, ctrl->val); + break; + + case V4L2_CID_EXPOSURE_AUTO: + ret = m5mols_set_exposure(info, ctrl->val); + break; + + case V4L2_CID_ISO_SENSITIVITY: + ret = m5mols_set_iso(info, ctrl->val); + break; + + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: + ret = m5mols_set_white_balance(info, ctrl->val); + break; + + case V4L2_CID_SATURATION: + ret = m5mols_set_saturation(info, ctrl->val); + break; + + case V4L2_CID_COLORFX: + ret = m5mols_set_color_effect(info, ctrl->val); + break; + + case V4L2_CID_WIDE_DYNAMIC_RANGE: + ret = m5mols_set_wdr(info, ctrl->val); + break; + + case V4L2_CID_IMAGE_STABILIZATION: + ret = m5mols_set_stabilization(info, ctrl->val); + break; + + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + ret = m5mols_write(sd, CAPP_JPEG_RATIO, ctrl->val); + break; + } + + if (ret == 0 && info->mode != last_mode) + ret = m5mols_set_mode(info, last_mode); + + return ret; +} + +static const struct v4l2_ctrl_ops m5mols_ctrl_ops = { + .g_volatile_ctrl = m5mols_g_volatile_ctrl, + .s_ctrl = m5mols_s_ctrl, +}; + +/* Supported manual ISO values */ +static const s64 iso_qmenu[] = { + /* AE_ISO: 0x01...0x07 (ISO: 50...3200) */ + 50000, 100000, 200000, 400000, 800000, 1600000, 3200000 +}; + +/* Supported Exposure Bias values, -2.0EV...+2.0EV */ +static const s64 ev_bias_qmenu[] = { + /* AE_INDEX: 0x00...0x08 */ + -2000, -1500, -1000, -500, 0, 500, 1000, 1500, 2000 +}; + +int m5mols_init_controls(struct v4l2_subdev *sd) +{ + struct m5mols_info *info = to_m5mols(sd); + u16 exposure_max; + u16 zoom_step; + int ret; + + /* Determine the firmware dependant control range and step values */ + ret = m5mols_read_u16(sd, AE_MAX_GAIN_MON, &exposure_max); + if (ret < 0) + return ret; + + zoom_step = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1; + v4l2_ctrl_handler_init(&info->handle, 20); + + info->auto_wb = v4l2_ctrl_new_std_menu(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, + 9, ~0x3fe, V4L2_WHITE_BALANCE_AUTO); + + /* Exposure control cluster */ + info->auto_exposure = v4l2_ctrl_new_std_menu(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, + 1, ~0x03, V4L2_EXPOSURE_AUTO); + + info->exposure = v4l2_ctrl_new_std(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_EXPOSURE, + 0, exposure_max, 1, exposure_max / 2); + + info->exposure_bias = v4l2_ctrl_new_int_menu(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_AUTO_EXPOSURE_BIAS, + ARRAY_SIZE(ev_bias_qmenu) - 1, + ARRAY_SIZE(ev_bias_qmenu)/2 - 1, + ev_bias_qmenu); + + info->metering = v4l2_ctrl_new_std_menu(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_METERING, + 2, ~0x7, V4L2_EXPOSURE_METERING_AVERAGE); + + /* ISO control cluster */ + info->auto_iso = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_ISO_SENSITIVITY_AUTO, 1, ~0x03, 1); + + info->iso = v4l2_ctrl_new_int_menu(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_ISO_SENSITIVITY, ARRAY_SIZE(iso_qmenu) - 1, + ARRAY_SIZE(iso_qmenu)/2 - 1, iso_qmenu); + + info->saturation = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_SATURATION, 1, 5, 1, 3); + + info->zoom = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_ZOOM_ABSOLUTE, 1, 70, zoom_step, 1); + + info->colorfx = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_COLORFX, 4, 0, V4L2_COLORFX_NONE); + + info->wdr = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_WIDE_DYNAMIC_RANGE, 0, 1, 1, 0); + + info->stabilization = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_IMAGE_STABILIZATION, 0, 1, 1, 0); + + info->jpeg_quality = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, 100, 1, 80); + + info->lock_3a = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_3A_LOCK, 0, 0x7, 0, 0); + + if (info->handle.error) { + int ret = info->handle.error; + v4l2_err(sd, "Failed to initialize controls: %d\n", ret); + v4l2_ctrl_handler_free(&info->handle); + return ret; + } + + v4l2_ctrl_auto_cluster(4, &info->auto_exposure, 1, false); + info->auto_iso->flags |= V4L2_CTRL_FLAG_VOLATILE | + V4L2_CTRL_FLAG_UPDATE; + v4l2_ctrl_auto_cluster(2, &info->auto_iso, 0, false); + + info->lock_3a->flags |= V4L2_CTRL_FLAG_VOLATILE; + + m5mols_set_ctrl_mode(info->auto_exposure, REG_PARAMETER); + m5mols_set_ctrl_mode(info->auto_wb, REG_PARAMETER); + m5mols_set_ctrl_mode(info->colorfx, REG_MONITOR); + + sd->ctrl_handler = &info->handle; + + return 0; +} diff --git a/drivers/media/i2c/m5mols/m5mols_core.c b/drivers/media/i2c/m5mols/m5mols_core.c new file mode 100644 index 00000000000..ac7d28b6ddf --- /dev/null +++ b/drivers/media/i2c/m5mols/m5mols_core.c @@ -0,0 +1,990 @@ +/* + * Driver for M-5MOLS 8M Pixel camera sensor with ISP + * + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * Author: HeungJun Kim + * + * Copyright (C) 2009 Samsung Electronics Co., Ltd. + * Author: Dongsoo Nathaniel Kim + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "m5mols.h" +#include "m5mols_reg.h" + +int m5mols_debug; +module_param(m5mols_debug, int, 0644); + +#define MODULE_NAME "M5MOLS" +#define M5MOLS_I2C_CHECK_RETRY 500 + +/* The regulator consumer names for external voltage regulators */ +static struct regulator_bulk_data supplies[] = { + { + .supply = "core", /* ARM core power, 1.2V */ + }, { + .supply = "dig_18", /* digital power 1, 1.8V */ + }, { + .supply = "d_sensor", /* sensor power 1, 1.8V */ + }, { + .supply = "dig_28", /* digital power 2, 2.8V */ + }, { + .supply = "a_sensor", /* analog power */ + }, { + .supply = "dig_12", /* digital power 3, 1.2V */ + }, +}; + +static struct v4l2_mbus_framefmt m5mols_default_ffmt[M5MOLS_RESTYPE_MAX] = { + [M5MOLS_RESTYPE_MONITOR] = { + .width = 1920, + .height = 1080, + .code = V4L2_MBUS_FMT_VYUY8_2X8, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_JPEG, + }, + [M5MOLS_RESTYPE_CAPTURE] = { + .width = 1920, + .height = 1080, + .code = V4L2_MBUS_FMT_JPEG_1X8, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_JPEG, + }, +}; +#define SIZE_DEFAULT_FFMT ARRAY_SIZE(m5mols_default_ffmt) + +static const struct m5mols_resolution m5mols_reg_res[] = { + { 0x01, M5MOLS_RESTYPE_MONITOR, 128, 96 }, /* SUB-QCIF */ + { 0x03, M5MOLS_RESTYPE_MONITOR, 160, 120 }, /* QQVGA */ + { 0x05, M5MOLS_RESTYPE_MONITOR, 176, 144 }, /* QCIF */ + { 0x06, M5MOLS_RESTYPE_MONITOR, 176, 176 }, + { 0x08, M5MOLS_RESTYPE_MONITOR, 240, 320 }, /* QVGA */ + { 0x09, M5MOLS_RESTYPE_MONITOR, 320, 240 }, /* QVGA */ + { 0x0c, M5MOLS_RESTYPE_MONITOR, 240, 400 }, /* WQVGA */ + { 0x0d, M5MOLS_RESTYPE_MONITOR, 400, 240 }, /* WQVGA */ + { 0x0e, M5MOLS_RESTYPE_MONITOR, 352, 288 }, /* CIF */ + { 0x13, M5MOLS_RESTYPE_MONITOR, 480, 360 }, + { 0x15, M5MOLS_RESTYPE_MONITOR, 640, 360 }, /* qHD */ + { 0x17, M5MOLS_RESTYPE_MONITOR, 640, 480 }, /* VGA */ + { 0x18, M5MOLS_RESTYPE_MONITOR, 720, 480 }, + { 0x1a, M5MOLS_RESTYPE_MONITOR, 800, 480 }, /* WVGA */ + { 0x1f, M5MOLS_RESTYPE_MONITOR, 800, 600 }, /* SVGA */ + { 0x21, M5MOLS_RESTYPE_MONITOR, 1280, 720 }, /* HD */ + { 0x25, M5MOLS_RESTYPE_MONITOR, 1920, 1080 }, /* 1080p */ + { 0x29, M5MOLS_RESTYPE_MONITOR, 3264, 2448 }, /* 2.63fps 8M */ + { 0x39, M5MOLS_RESTYPE_MONITOR, 800, 602 }, /* AHS_MON debug */ + + { 0x02, M5MOLS_RESTYPE_CAPTURE, 320, 240 }, /* QVGA */ + { 0x04, M5MOLS_RESTYPE_CAPTURE, 400, 240 }, /* WQVGA */ + { 0x07, M5MOLS_RESTYPE_CAPTURE, 480, 360 }, + { 0x08, M5MOLS_RESTYPE_CAPTURE, 640, 360 }, /* qHD */ + { 0x09, M5MOLS_RESTYPE_CAPTURE, 640, 480 }, /* VGA */ + { 0x0a, M5MOLS_RESTYPE_CAPTURE, 800, 480 }, /* WVGA */ + { 0x10, M5MOLS_RESTYPE_CAPTURE, 1280, 720 }, /* HD */ + { 0x14, M5MOLS_RESTYPE_CAPTURE, 1280, 960 }, /* 1M */ + { 0x17, M5MOLS_RESTYPE_CAPTURE, 1600, 1200 }, /* 2M */ + { 0x19, M5MOLS_RESTYPE_CAPTURE, 1920, 1080 }, /* Full-HD */ + { 0x1a, M5MOLS_RESTYPE_CAPTURE, 2048, 1152 }, /* 3Mega */ + { 0x1b, M5MOLS_RESTYPE_CAPTURE, 2048, 1536 }, + { 0x1c, M5MOLS_RESTYPE_CAPTURE, 2560, 1440 }, /* 4Mega */ + { 0x1d, M5MOLS_RESTYPE_CAPTURE, 2560, 1536 }, + { 0x1f, M5MOLS_RESTYPE_CAPTURE, 2560, 1920 }, /* 5Mega */ + { 0x21, M5MOLS_RESTYPE_CAPTURE, 3264, 1836 }, /* 6Mega */ + { 0x22, M5MOLS_RESTYPE_CAPTURE, 3264, 1960 }, + { 0x25, M5MOLS_RESTYPE_CAPTURE, 3264, 2448 }, /* 8Mega */ +}; + +/** + * m5mols_swap_byte - an byte array to integer conversion function + * @size: size in bytes of I2C packet defined in the M-5MOLS datasheet + * + * Convert I2C data byte array with performing any required byte + * reordering to assure proper values for each data type, regardless + * of the architecture endianness. + */ +static u32 m5mols_swap_byte(u8 *data, u8 length) +{ + if (length == 1) + return *data; + else if (length == 2) + return be16_to_cpu(*((u16 *)data)); + else + return be32_to_cpu(*((u32 *)data)); +} + +/** + * m5mols_read - I2C read function + * @reg: combination of size, category and command for the I2C packet + * @size: desired size of I2C packet + * @val: read value + * + * Returns 0 on success, or else negative errno. + */ +static int m5mols_read(struct v4l2_subdev *sd, u32 size, u32 reg, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct m5mols_info *info = to_m5mols(sd); + u8 rbuf[M5MOLS_I2C_MAX_SIZE + 1]; + u8 category = I2C_CATEGORY(reg); + u8 cmd = I2C_COMMAND(reg); + struct i2c_msg msg[2]; + u8 wbuf[5]; + int ret; + + if (!client->adapter) + return -ENODEV; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 5; + msg[0].buf = wbuf; + wbuf[0] = 5; + wbuf[1] = M5MOLS_BYTE_READ; + wbuf[2] = category; + wbuf[3] = cmd; + wbuf[4] = size; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = size + 1; + msg[1].buf = rbuf; + + /* minimum stabilization time */ + usleep_range(200, 200); + + ret = i2c_transfer(client->adapter, msg, 2); + + if (ret == 2) { + *val = m5mols_swap_byte(&rbuf[1], size); + return 0; + } + + if (info->isp_ready) + v4l2_err(sd, "read failed: size:%d cat:%02x cmd:%02x. %d\n", + size, category, cmd, ret); + + return ret < 0 ? ret : -EIO; +} + +int m5mols_read_u8(struct v4l2_subdev *sd, u32 reg, u8 *val) +{ + u32 val_32; + int ret; + + if (I2C_SIZE(reg) != 1) { + v4l2_err(sd, "Wrong data size\n"); + return -EINVAL; + } + + ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32); + if (ret) + return ret; + + *val = (u8)val_32; + return ret; +} + +int m5mols_read_u16(struct v4l2_subdev *sd, u32 reg, u16 *val) +{ + u32 val_32; + int ret; + + if (I2C_SIZE(reg) != 2) { + v4l2_err(sd, "Wrong data size\n"); + return -EINVAL; + } + + ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32); + if (ret) + return ret; + + *val = (u16)val_32; + return ret; +} + +int m5mols_read_u32(struct v4l2_subdev *sd, u32 reg, u32 *val) +{ + if (I2C_SIZE(reg) != 4) { + v4l2_err(sd, "Wrong data size\n"); + return -EINVAL; + } + + return m5mols_read(sd, I2C_SIZE(reg), reg, val); +} + +/** + * m5mols_write - I2C command write function + * @reg: combination of size, category and command for the I2C packet + * @val: value to write + * + * Returns 0 on success, or else negative errno. + */ +int m5mols_write(struct v4l2_subdev *sd, u32 reg, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct m5mols_info *info = to_m5mols(sd); + u8 wbuf[M5MOLS_I2C_MAX_SIZE + 4]; + u8 category = I2C_CATEGORY(reg); + u8 cmd = I2C_COMMAND(reg); + u8 size = I2C_SIZE(reg); + u32 *buf = (u32 *)&wbuf[4]; + struct i2c_msg msg[1]; + int ret; + + if (!client->adapter) + return -ENODEV; + + if (size != 1 && size != 2 && size != 4) { + v4l2_err(sd, "Wrong data size\n"); + return -EINVAL; + } + + msg->addr = client->addr; + msg->flags = 0; + msg->len = (u16)size + 4; + msg->buf = wbuf; + wbuf[0] = size + 4; + wbuf[1] = M5MOLS_BYTE_WRITE; + wbuf[2] = category; + wbuf[3] = cmd; + + *buf = m5mols_swap_byte((u8 *)&val, size); + + usleep_range(200, 200); + + ret = i2c_transfer(client->adapter, msg, 1); + if (ret == 1) + return 0; + + if (info->isp_ready) + v4l2_err(sd, "write failed: cat:%02x cmd:%02x ret:%d\n", + category, cmd, ret); + + return ret < 0 ? ret : -EIO; +} + +/** + * m5mols_busy_wait - Busy waiting with I2C register polling + * @reg: the I2C_REG() address of an 8-bit status register to check + * @value: expected status register value + * @mask: bit mask for the read status register value + * @timeout: timeout in miliseconds, or -1 for default timeout + * + * The @reg register value is ORed with @mask before comparing with @value. + * + * Return: 0 if the requested condition became true within less than + * @timeout ms, or else negative errno. + */ +int m5mols_busy_wait(struct v4l2_subdev *sd, u32 reg, u32 value, u32 mask, + int timeout) +{ + int ms = timeout < 0 ? M5MOLS_BUSY_WAIT_DEF_TIMEOUT : timeout; + unsigned long end = jiffies + msecs_to_jiffies(ms); + u8 status; + + do { + int ret = m5mols_read_u8(sd, reg, &status); + + if (ret < 0 && !(mask & M5MOLS_I2C_RDY_WAIT_FL)) + return ret; + if (!ret && (status & mask & 0xff) == (value & 0xff)) + return 0; + usleep_range(100, 250); + } while (ms > 0 && time_is_after_jiffies(end)); + + return -EBUSY; +} + +/** + * m5mols_enable_interrupt - Clear interrupt pending bits and unmask interrupts + * + * Before writing desired interrupt value the INT_FACTOR register should + * be read to clear pending interrupts. + */ +int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg) +{ + struct m5mols_info *info = to_m5mols(sd); + u8 mask = is_available_af(info) ? REG_INT_AF : 0; + u8 dummy; + int ret; + + ret = m5mols_read_u8(sd, SYSTEM_INT_FACTOR, &dummy); + if (!ret) + ret = m5mols_write(sd, SYSTEM_INT_ENABLE, reg & ~mask); + return ret; +} + +int m5mols_wait_interrupt(struct v4l2_subdev *sd, u8 irq_mask, u32 timeout) +{ + struct m5mols_info *info = to_m5mols(sd); + + int ret = wait_event_interruptible_timeout(info->irq_waitq, + atomic_add_unless(&info->irq_done, -1, 0), + msecs_to_jiffies(timeout)); + if (ret <= 0) + return ret ? ret : -ETIMEDOUT; + + return m5mols_busy_wait(sd, SYSTEM_INT_FACTOR, irq_mask, + M5MOLS_I2C_RDY_WAIT_FL | irq_mask, -1); +} + +/** + * m5mols_reg_mode - Write the mode and check busy status + * + * It always accompanies a little delay changing the M-5MOLS mode, so it is + * needed checking current busy status to guarantee right mode. + */ +static int m5mols_reg_mode(struct v4l2_subdev *sd, u8 mode) +{ + int ret = m5mols_write(sd, SYSTEM_SYSMODE, mode); + if (ret < 0) + return ret; + return m5mols_busy_wait(sd, SYSTEM_SYSMODE, mode, 0xff, + M5MOLS_MODE_CHANGE_TIMEOUT); +} + +/** + * m5mols_set_mode - set the M-5MOLS controller mode + * @mode: the required operation mode + * + * The commands of M-5MOLS are grouped into specific modes. Each functionality + * can be guaranteed only when the sensor is operating in mode which a command + * belongs to. + */ +int m5mols_set_mode(struct m5mols_info *info, u8 mode) +{ + struct v4l2_subdev *sd = &info->sd; + int ret = -EINVAL; + u8 reg; + + if (mode < REG_PARAMETER || mode > REG_CAPTURE) + return ret; + + ret = m5mols_read_u8(sd, SYSTEM_SYSMODE, ®); + if (ret || reg == mode) + return ret; + + switch (reg) { + case REG_PARAMETER: + ret = m5mols_reg_mode(sd, REG_MONITOR); + if (mode == REG_MONITOR) + break; + if (!ret) + ret = m5mols_reg_mode(sd, REG_CAPTURE); + break; + + case REG_MONITOR: + if (mode == REG_PARAMETER) { + ret = m5mols_reg_mode(sd, REG_PARAMETER); + break; + } + + ret = m5mols_reg_mode(sd, REG_CAPTURE); + break; + + case REG_CAPTURE: + ret = m5mols_reg_mode(sd, REG_MONITOR); + if (mode == REG_MONITOR) + break; + if (!ret) + ret = m5mols_reg_mode(sd, REG_PARAMETER); + break; + + default: + v4l2_warn(sd, "Wrong mode: %d\n", mode); + } + + if (!ret) + info->mode = mode; + + return ret; +} + +/** + * m5mols_get_version - retrieve full revisions information of M-5MOLS + * + * The version information includes revisions of hardware and firmware, + * AutoFocus alghorithm version and the version string. + */ +static int m5mols_get_version(struct v4l2_subdev *sd) +{ + struct m5mols_info *info = to_m5mols(sd); + struct m5mols_version *ver = &info->ver; + u8 *str = ver->str; + int i; + int ret; + + ret = m5mols_read_u8(sd, SYSTEM_VER_CUSTOMER, &ver->customer); + if (!ret) + ret = m5mols_read_u8(sd, SYSTEM_VER_PROJECT, &ver->project); + if (!ret) + ret = m5mols_read_u16(sd, SYSTEM_VER_FIRMWARE, &ver->fw); + if (!ret) + ret = m5mols_read_u16(sd, SYSTEM_VER_HARDWARE, &ver->hw); + if (!ret) + ret = m5mols_read_u16(sd, SYSTEM_VER_PARAMETER, &ver->param); + if (!ret) + ret = m5mols_read_u16(sd, SYSTEM_VER_AWB, &ver->awb); + if (!ret) + ret = m5mols_read_u8(sd, AF_VERSION, &ver->af); + if (ret) + return ret; + + for (i = 0; i < VERSION_STRING_SIZE; i++) { + ret = m5mols_read_u8(sd, SYSTEM_VER_STRING, &str[i]); + if (ret) + return ret; + } + + ver->fw = be16_to_cpu(ver->fw); + ver->hw = be16_to_cpu(ver->hw); + ver->param = be16_to_cpu(ver->param); + ver->awb = be16_to_cpu(ver->awb); + + v4l2_info(sd, "Manufacturer\t[%s]\n", + is_manufacturer(info, REG_SAMSUNG_ELECTRO) ? + "Samsung Electro-Machanics" : + is_manufacturer(info, REG_SAMSUNG_OPTICS) ? + "Samsung Fiber-Optics" : + is_manufacturer(info, REG_SAMSUNG_TECHWIN) ? + "Samsung Techwin" : "None"); + v4l2_info(sd, "Customer/Project\t[0x%02x/0x%02x]\n", + info->ver.customer, info->ver.project); + + if (!is_available_af(info)) + v4l2_info(sd, "No support Auto Focus on this firmware\n"); + + return ret; +} + +/** + * __find_restype - Lookup M-5MOLS resolution type according to pixel code + * @code: pixel code + */ +static enum m5mols_restype __find_restype(enum v4l2_mbus_pixelcode code) +{ + enum m5mols_restype type = M5MOLS_RESTYPE_MONITOR; + + do { + if (code == m5mols_default_ffmt[type].code) + return type; + } while (type++ != SIZE_DEFAULT_FFMT); + + return 0; +} + +/** + * __find_resolution - Lookup preset and type of M-5MOLS's resolution + * @mf: pixel format to find/negotiate the resolution preset for + * @type: M-5MOLS resolution type + * @resolution: M-5MOLS resolution preset register value + * + * Find nearest resolution matching resolution preset and adjust mf + * to supported values. + */ +static int __find_resolution(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf, + enum m5mols_restype *type, + u32 *resolution) +{ + const struct m5mols_resolution *fsize = &m5mols_reg_res[0]; + const struct m5mols_resolution *match = NULL; + enum m5mols_restype stype = __find_restype(mf->code); + int i = ARRAY_SIZE(m5mols_reg_res); + unsigned int min_err = ~0; + + while (i--) { + int err; + if (stype == fsize->type) { + err = abs(fsize->width - mf->width) + + abs(fsize->height - mf->height); + + if (err < min_err) { + min_err = err; + match = fsize; + } + } + fsize++; + } + if (match) { + mf->width = match->width; + mf->height = match->height; + *resolution = match->reg; + *type = stype; + return 0; + } + + return -EINVAL; +} + +static struct v4l2_mbus_framefmt *__find_format(struct m5mols_info *info, + struct v4l2_subdev_fh *fh, + enum v4l2_subdev_format_whence which, + enum m5mols_restype type) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL; + + return &info->ffmt[type]; +} + +static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct m5mols_info *info = to_m5mols(sd); + struct v4l2_mbus_framefmt *format; + + format = __find_format(info, fh, fmt->which, info->res_type); + if (!format) + return -EINVAL; + + fmt->format = *format; + return 0; +} + +static int m5mols_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct m5mols_info *info = to_m5mols(sd); + struct v4l2_mbus_framefmt *format = &fmt->format; + struct v4l2_mbus_framefmt *sfmt; + enum m5mols_restype type; + u32 resolution = 0; + int ret; + + ret = __find_resolution(sd, format, &type, &resolution); + if (ret < 0) + return ret; + + sfmt = __find_format(info, fh, fmt->which, type); + if (!sfmt) + return 0; + + + format->code = m5mols_default_ffmt[type].code; + format->colorspace = V4L2_COLORSPACE_JPEG; + format->field = V4L2_FIELD_NONE; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + *sfmt = *format; + info->resolution = resolution; + info->res_type = type; + } + + return 0; +} + +static int m5mols_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (!code || code->index >= SIZE_DEFAULT_FFMT) + return -EINVAL; + + code->code = m5mols_default_ffmt[code->index].code; + + return 0; +} + +static struct v4l2_subdev_pad_ops m5mols_pad_ops = { + .enum_mbus_code = m5mols_enum_mbus_code, + .get_fmt = m5mols_get_fmt, + .set_fmt = m5mols_set_fmt, +}; + +/** + * m5mols_restore_controls - Apply current control values to the registers + * + * m5mols_do_scenemode() handles all parameters for which there is yet no + * individual control. It should be replaced at some point by setting each + * control individually, in required register set up order. + */ +int m5mols_restore_controls(struct m5mols_info *info) +{ + int ret; + + if (info->ctrl_sync) + return 0; + + ret = m5mols_do_scenemode(info, REG_SCENE_NORMAL); + if (ret) + return ret; + + ret = v4l2_ctrl_handler_setup(&info->handle); + info->ctrl_sync = !ret; + + return ret; +} + +/** + * m5mols_start_monitor - Start the monitor mode + * + * Before applying the controls setup the resolution and frame rate + * in PARAMETER mode, and then switch over to MONITOR mode. + */ +static int m5mols_start_monitor(struct m5mols_info *info) +{ + struct v4l2_subdev *sd = &info->sd; + int ret; + + ret = m5mols_set_mode(info, REG_PARAMETER); + if (!ret) + ret = m5mols_write(sd, PARM_MON_SIZE, info->resolution); + if (!ret) + ret = m5mols_write(sd, PARM_MON_FPS, REG_FPS_30); + if (!ret) + ret = m5mols_set_mode(info, REG_MONITOR); + if (!ret) + ret = m5mols_restore_controls(info); + + return ret; +} + +static int m5mols_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct m5mols_info *info = to_m5mols(sd); + u32 code = info->ffmt[info->res_type].code; + + if (enable) { + int ret = -EINVAL; + + if (is_code(code, M5MOLS_RESTYPE_MONITOR)) + ret = m5mols_start_monitor(info); + if (is_code(code, M5MOLS_RESTYPE_CAPTURE)) + ret = m5mols_start_capture(info); + + return ret; + } + + return m5mols_set_mode(info, REG_PARAMETER); +} + +static const struct v4l2_subdev_video_ops m5mols_video_ops = { + .s_stream = m5mols_s_stream, +}; + +static int m5mols_sensor_power(struct m5mols_info *info, bool enable) +{ + struct v4l2_subdev *sd = &info->sd; + struct i2c_client *client = v4l2_get_subdevdata(sd); + const struct m5mols_platform_data *pdata = info->pdata; + int ret; + + if (info->power == enable) + return 0; + + if (enable) { + if (info->set_power) { + ret = info->set_power(&client->dev, 1); + if (ret) + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies); + if (ret) { + info->set_power(&client->dev, 0); + return ret; + } + + gpio_set_value(pdata->gpio_reset, !pdata->reset_polarity); + info->power = 1; + + return ret; + } + + ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies); + if (ret) + return ret; + + if (info->set_power) + info->set_power(&client->dev, 0); + + gpio_set_value(pdata->gpio_reset, pdata->reset_polarity); + + info->isp_ready = 0; + info->power = 0; + + return ret; +} + +/* m5mols_update_fw - optional firmware update routine */ +int __attribute__ ((weak)) m5mols_update_fw(struct v4l2_subdev *sd, + int (*set_power)(struct m5mols_info *, bool)) +{ + return 0; +} + +/** + * m5mols_fw_start - M-5MOLS internal ARM controller initialization + * + * Execute the M-5MOLS internal ARM controller initialization sequence. + * This function should be called after the supply voltage has been + * applied and before any requests to the device are made. + */ +static int m5mols_fw_start(struct v4l2_subdev *sd) +{ + struct m5mols_info *info = to_m5mols(sd); + int ret; + + atomic_set(&info->irq_done, 0); + /* Wait until I2C slave is initialized in Flash Writer mode */ + ret = m5mols_busy_wait(sd, FLASH_CAM_START, REG_IN_FLASH_MODE, + M5MOLS_I2C_RDY_WAIT_FL | 0xff, -1); + if (!ret) + ret = m5mols_write(sd, FLASH_CAM_START, REG_START_ARM_BOOT); + if (!ret) + ret = m5mols_wait_interrupt(sd, REG_INT_MODE, 2000); + if (ret < 0) + return ret; + + info->isp_ready = 1; + + ret = m5mols_get_version(sd); + if (!ret) + ret = m5mols_update_fw(sd, m5mols_sensor_power); + if (ret) + return ret; + + v4l2_dbg(1, m5mols_debug, sd, "Success ARM Booting\n"); + + ret = m5mols_write(sd, PARM_INTERFACE, REG_INTERFACE_MIPI); + if (!ret) + ret = m5mols_enable_interrupt(sd, + REG_INT_AF | REG_INT_CAPTURE); + + return ret; +} + +/** + * m5mols_s_power - Main sensor power control function + * + * To prevent breaking the lens when the sensor is powered off the Soft-Landing + * algorithm is called where available. The Soft-Landing algorithm availability + * dependends on the firmware provider. + */ +static int m5mols_s_power(struct v4l2_subdev *sd, int on) +{ + struct m5mols_info *info = to_m5mols(sd); + int ret; + + if (on) { + ret = m5mols_sensor_power(info, true); + if (!ret) + ret = m5mols_fw_start(sd); + return ret; + } + + if (is_manufacturer(info, REG_SAMSUNG_TECHWIN)) { + ret = m5mols_set_mode(info, REG_MONITOR); + if (!ret) + ret = m5mols_write(sd, AF_EXECUTE, REG_AF_STOP); + if (!ret) + ret = m5mols_write(sd, AF_MODE, REG_AF_POWEROFF); + if (!ret) + ret = m5mols_busy_wait(sd, SYSTEM_STATUS, REG_AF_IDLE, + 0xff, -1); + if (ret < 0) + v4l2_warn(sd, "Soft landing lens failed\n"); + } + + ret = m5mols_sensor_power(info, false); + info->ctrl_sync = 0; + + return ret; +} + +static int m5mols_log_status(struct v4l2_subdev *sd) +{ + struct m5mols_info *info = to_m5mols(sd); + + v4l2_ctrl_handler_log_status(&info->handle, sd->name); + + return 0; +} + +static const struct v4l2_subdev_core_ops m5mols_core_ops = { + .s_power = m5mols_s_power, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .log_status = m5mols_log_status, +}; + +/* + * V4L2 subdev internal operations + */ +static int m5mols_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); + + *format = m5mols_default_ffmt[0]; + return 0; +} + +static const struct v4l2_subdev_internal_ops m5mols_subdev_internal_ops = { + .open = m5mols_open, +}; + +static const struct v4l2_subdev_ops m5mols_ops = { + .core = &m5mols_core_ops, + .pad = &m5mols_pad_ops, + .video = &m5mols_video_ops, +}; + +static irqreturn_t m5mols_irq_handler(int irq, void *data) +{ + struct m5mols_info *info = to_m5mols(data); + + atomic_set(&info->irq_done, 1); + wake_up_interruptible(&info->irq_waitq); + + return IRQ_HANDLED; +} + +static int __devinit m5mols_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct m5mols_platform_data *pdata = client->dev.platform_data; + struct m5mols_info *info; + struct v4l2_subdev *sd; + int ret; + + if (pdata == NULL) { + dev_err(&client->dev, "No platform data\n"); + return -EINVAL; + } + + if (!gpio_is_valid(pdata->gpio_reset)) { + dev_err(&client->dev, "No valid RESET GPIO specified\n"); + return -EINVAL; + } + + if (!client->irq) { + dev_err(&client->dev, "Interrupt not assigned\n"); + return -EINVAL; + } + + info = kzalloc(sizeof(struct m5mols_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->pdata = pdata; + info->set_power = pdata->set_power; + + ret = gpio_request(pdata->gpio_reset, "M5MOLS_NRST"); + if (ret) { + dev_err(&client->dev, "Failed to request gpio: %d\n", ret); + goto out_free; + } + gpio_direction_output(pdata->gpio_reset, pdata->reset_polarity); + + ret = regulator_bulk_get(&client->dev, ARRAY_SIZE(supplies), supplies); + if (ret) { + dev_err(&client->dev, "Failed to get regulators: %d\n", ret); + goto out_gpio; + } + + sd = &info->sd; + v4l2_i2c_subdev_init(sd, client, &m5mols_ops); + strlcpy(sd->name, MODULE_NAME, sizeof(sd->name)); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + sd->internal_ops = &m5mols_subdev_internal_ops; + info->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&sd->entity, 1, &info->pad, 0); + if (ret < 0) + goto out_reg; + sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; + + init_waitqueue_head(&info->irq_waitq); + ret = request_irq(client->irq, m5mols_irq_handler, + IRQF_TRIGGER_RISING, MODULE_NAME, sd); + if (ret) { + dev_err(&client->dev, "Interrupt request failed: %d\n", ret); + goto out_me; + } + info->res_type = M5MOLS_RESTYPE_MONITOR; + info->ffmt[0] = m5mols_default_ffmt[0]; + info->ffmt[1] = m5mols_default_ffmt[1]; + + ret = m5mols_sensor_power(info, true); + if (ret) + goto out_me; + + ret = m5mols_fw_start(sd); + if (!ret) + ret = m5mols_init_controls(sd); + + m5mols_sensor_power(info, false); + if (!ret) + return 0; +out_me: + media_entity_cleanup(&sd->entity); +out_reg: + regulator_bulk_free(ARRAY_SIZE(supplies), supplies); +out_gpio: + gpio_free(pdata->gpio_reset); +out_free: + kfree(info); + return ret; +} + +static int __devexit m5mols_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct m5mols_info *info = to_m5mols(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); + free_irq(client->irq, sd); + + regulator_bulk_free(ARRAY_SIZE(supplies), supplies); + gpio_free(info->pdata->gpio_reset); + media_entity_cleanup(&sd->entity); + kfree(info); + return 0; +} + +static const struct i2c_device_id m5mols_id[] = { + { MODULE_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, m5mols_id); + +static struct i2c_driver m5mols_i2c_driver = { + .driver = { + .name = MODULE_NAME, + }, + .probe = m5mols_probe, + .remove = __devexit_p(m5mols_remove), + .id_table = m5mols_id, +}; + +module_i2c_driver(m5mols_i2c_driver); + +MODULE_AUTHOR("HeungJun Kim "); +MODULE_AUTHOR("Dongsoo Kim "); +MODULE_DESCRIPTION("Fujitsu M-5MOLS 8M Pixel camera driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/m5mols/m5mols_reg.h b/drivers/media/i2c/m5mols/m5mols_reg.h new file mode 100644 index 00000000000..14d4be72aef --- /dev/null +++ b/drivers/media/i2c/m5mols/m5mols_reg.h @@ -0,0 +1,362 @@ +/* + * Register map for M-5MOLS 8M Pixel camera sensor with ISP + * + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * Author: HeungJun Kim + * + * Copyright (C) 2009 Samsung Electronics Co., Ltd. + * Author: Dongsoo Nathaniel Kim + * + * 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. + */ + +#ifndef M5MOLS_REG_H +#define M5MOLS_REG_H + +#define M5MOLS_I2C_MAX_SIZE 4 +#define M5MOLS_BYTE_READ 0x01 +#define M5MOLS_BYTE_WRITE 0x02 + +#define I2C_CATEGORY(__cat) ((__cat >> 16) & 0xff) +#define I2C_COMMAND(__comm) ((__comm >> 8) & 0xff) +#define I2C_SIZE(__reg_s) ((__reg_s) & 0xff) +#define I2C_REG(__cat, __cmd, __reg_s) ((__cat << 16) | (__cmd << 8) | __reg_s) + +/* + * Category section register + * + * The category means set including relevant command of M-5MOLS. + */ +#define CAT_SYSTEM 0x00 +#define CAT_PARAM 0x01 +#define CAT_MONITOR 0x02 +#define CAT_AE 0x03 +#define CAT_WB 0x06 +#define CAT_EXIF 0x07 +#define CAT_FD 0x09 +#define CAT_LENS 0x0a +#define CAT_CAPT_PARM 0x0b +#define CAT_CAPT_CTRL 0x0c +#define CAT_FLASH 0x0f /* related to FW, revisions, booting */ + +/* + * Category 0 - SYSTEM mode + * + * The SYSTEM mode in the M-5MOLS means area available to handle with the whole + * & all-round system of sensor. It deals with version/interrupt/setting mode & + * even sensor's status. Especially, the M-5MOLS sensor with ISP varies by + * packaging & manufacturer, even the customer and project code. And the + * function details may vary among them. The version information helps to + * determine what methods shall be used in the driver. + * + * There is many registers between customer version address and awb one. For + * more specific contents, see definition if file m5mols.h. + */ +#define SYSTEM_VER_CUSTOMER I2C_REG(CAT_SYSTEM, 0x00, 1) +#define SYSTEM_VER_PROJECT I2C_REG(CAT_SYSTEM, 0x01, 1) +#define SYSTEM_VER_FIRMWARE I2C_REG(CAT_SYSTEM, 0x02, 2) +#define SYSTEM_VER_HARDWARE I2C_REG(CAT_SYSTEM, 0x04, 2) +#define SYSTEM_VER_PARAMETER I2C_REG(CAT_SYSTEM, 0x06, 2) +#define SYSTEM_VER_AWB I2C_REG(CAT_SYSTEM, 0x08, 2) + +#define SYSTEM_SYSMODE I2C_REG(CAT_SYSTEM, 0x0b, 1) +#define REG_SYSINIT 0x00 /* SYSTEM mode */ +#define REG_PARAMETER 0x01 /* PARAMETER mode */ +#define REG_MONITOR 0x02 /* MONITOR mode */ +#define REG_CAPTURE 0x03 /* CAPTURE mode */ + +#define SYSTEM_CMD(__cmd) I2C_REG(CAT_SYSTEM, cmd, 1) +#define SYSTEM_VER_STRING I2C_REG(CAT_SYSTEM, 0x0a, 1) +#define REG_SAMSUNG_ELECTRO "SE" /* Samsung Electro-Mechanics */ +#define REG_SAMSUNG_OPTICS "OP" /* Samsung Fiber-Optics */ +#define REG_SAMSUNG_TECHWIN "TB" /* Samsung Techwin */ +/* SYSTEM mode status */ +#define SYSTEM_STATUS I2C_REG(CAT_SYSTEM, 0x0c, 1) + +/* Interrupt pending register */ +#define SYSTEM_INT_FACTOR I2C_REG(CAT_SYSTEM, 0x10, 1) +/* interrupt enable register */ +#define SYSTEM_INT_ENABLE I2C_REG(CAT_SYSTEM, 0x11, 1) +#define REG_INT_MODE (1 << 0) +#define REG_INT_AF (1 << 1) +#define REG_INT_ZOOM (1 << 2) +#define REG_INT_CAPTURE (1 << 3) +#define REG_INT_FRAMESYNC (1 << 4) +#define REG_INT_FD (1 << 5) +#define REG_INT_LENS_INIT (1 << 6) +#define REG_INT_SOUND (1 << 7) +#define REG_INT_MASK 0x0f + +/* + * category 1 - PARAMETER mode + * + * This category supports function of camera features of M-5MOLS. It means we + * can handle with preview(MONITOR) resolution size/frame per second/interface + * between the sensor and the Application Processor/even the image effect. + */ + +/* Resolution in the MONITOR mode */ +#define PARM_MON_SIZE I2C_REG(CAT_PARAM, 0x01, 1) + +/* Frame rate */ +#define PARM_MON_FPS I2C_REG(CAT_PARAM, 0x02, 1) +#define REG_FPS_30 0x02 + +/* Video bus between the sensor and a host processor */ +#define PARM_INTERFACE I2C_REG(CAT_PARAM, 0x00, 1) +#define REG_INTERFACE_MIPI 0x02 + +/* Image effects */ +#define PARM_EFFECT I2C_REG(CAT_PARAM, 0x0b, 1) +#define REG_EFFECT_OFF 0x00 +#define REG_EFFECT_NEGA 0x01 +#define REG_EFFECT_EMBOSS 0x06 +#define REG_EFFECT_OUTLINE 0x07 +#define REG_EFFECT_WATERCOLOR 0x08 + +/* + * Category 2 - MONITOR mode + * + * The MONITOR mode is same as preview mode as we said. The M-5MOLS has another + * mode named "Preview", but this preview mode is used at the case specific + * vider-recording mode. This mmode supports only YUYV format. On the other + * hand, the JPEG & RAW formats is supports by CAPTURE mode. And, there are + * another options like zoom/color effect(different with effect in PARAMETER + * mode)/anti hand shaking algorithm. + */ + +/* Target digital zoom position */ +#define MON_ZOOM I2C_REG(CAT_MONITOR, 0x01, 1) + +/* CR value for color effect */ +#define MON_CFIXR I2C_REG(CAT_MONITOR, 0x0a, 1) +/* CB value for color effect */ +#define MON_CFIXB I2C_REG(CAT_MONITOR, 0x09, 1) +#define REG_CFIXB_SEPIA 0xd8 +#define REG_CFIXR_SEPIA 0x18 + +#define MON_EFFECT I2C_REG(CAT_MONITOR, 0x0b, 1) +#define REG_COLOR_EFFECT_OFF 0x00 +#define REG_COLOR_EFFECT_ON 0x01 + +/* Chroma enable */ +#define MON_CHROMA_EN I2C_REG(CAT_MONITOR, 0x10, 1) +/* Chroma level */ +#define MON_CHROMA_LVL I2C_REG(CAT_MONITOR, 0x0f, 1) +#define REG_CHROMA_OFF 0x00 +#define REG_CHROMA_ON 0x01 + +/* Sharpness on/off */ +#define MON_EDGE_EN I2C_REG(CAT_MONITOR, 0x12, 1) +/* Sharpness level */ +#define MON_EDGE_LVL I2C_REG(CAT_MONITOR, 0x11, 1) +#define REG_EDGE_OFF 0x00 +#define REG_EDGE_ON 0x01 + +/* Set color tone (contrast) */ +#define MON_TONE_CTL I2C_REG(CAT_MONITOR, 0x25, 1) + +/* + * Category 3 - Auto Exposure + * + * The M-5MOLS exposure capbility is detailed as which is similar to digital + * camera. This category supports AE locking/various AE mode(range of exposure) + * /ISO/flickering/EV bias/shutter/meteoring, and anything else. And the + * maximum/minimum exposure gain value depending on M-5MOLS firmware, may be + * different. So, this category also provide getting the max/min values. And, + * each MONITOR and CAPTURE mode has each gain/shutter/max exposure values. + */ + +/* Auto Exposure locking */ +#define AE_LOCK I2C_REG(CAT_AE, 0x00, 1) +#define REG_AE_UNLOCK 0x00 +#define REG_AE_LOCK 0x01 + +/* Auto Exposure algorithm mode */ +#define AE_MODE I2C_REG(CAT_AE, 0x01, 1) +#define REG_AE_OFF 0x00 /* AE off */ +#define REG_AE_ALL 0x01 /* calc AE in all block integral */ +#define REG_AE_CENTER 0x03 /* calc AE in center weighted */ +#define REG_AE_SPOT 0x06 /* calc AE in specific spot */ + +#define AE_ISO I2C_REG(CAT_AE, 0x05, 1) +#define REG_ISO_AUTO 0x00 +#define REG_ISO_50 0x01 +#define REG_ISO_100 0x02 +#define REG_ISO_200 0x03 +#define REG_ISO_400 0x04 +#define REG_ISO_800 0x05 + +/* EV (scenemode) preset for MONITOR */ +#define AE_EV_PRESET_MONITOR I2C_REG(CAT_AE, 0x0a, 1) +/* EV (scenemode) preset for CAPTURE */ +#define AE_EV_PRESET_CAPTURE I2C_REG(CAT_AE, 0x0b, 1) +#define REG_SCENE_NORMAL 0x00 +#define REG_SCENE_PORTRAIT 0x01 +#define REG_SCENE_LANDSCAPE 0x02 +#define REG_SCENE_SPORTS 0x03 +#define REG_SCENE_PARTY_INDOOR 0x04 +#define REG_SCENE_BEACH_SNOW 0x05 +#define REG_SCENE_SUNSET 0x06 +#define REG_SCENE_DAWN_DUSK 0x07 +#define REG_SCENE_FALL 0x08 +#define REG_SCENE_NIGHT 0x09 +#define REG_SCENE_AGAINST_LIGHT 0x0a +#define REG_SCENE_FIRE 0x0b +#define REG_SCENE_TEXT 0x0c +#define REG_SCENE_CANDLE 0x0d + +/* Manual gain in MONITOR mode */ +#define AE_MAN_GAIN_MON I2C_REG(CAT_AE, 0x12, 2) +/* Maximum gain in MONITOR mode */ +#define AE_MAX_GAIN_MON I2C_REG(CAT_AE, 0x1a, 2) +/* Manual gain in CAPTURE mode */ +#define AE_MAN_GAIN_CAP I2C_REG(CAT_AE, 0x26, 2) + +#define AE_INDEX I2C_REG(CAT_AE, 0x38, 1) +#define REG_AE_INDEX_20_NEG 0x00 +#define REG_AE_INDEX_15_NEG 0x01 +#define REG_AE_INDEX_10_NEG 0x02 +#define REG_AE_INDEX_05_NEG 0x03 +#define REG_AE_INDEX_00 0x04 +#define REG_AE_INDEX_05_POS 0x05 +#define REG_AE_INDEX_10_POS 0x06 +#define REG_AE_INDEX_15_POS 0x07 +#define REG_AE_INDEX_20_POS 0x08 + +/* + * Category 6 - White Balance + */ + +/* Auto Whitebalance locking */ +#define AWB_LOCK I2C_REG(CAT_WB, 0x00, 1) +#define REG_AWB_UNLOCK 0x00 +#define REG_AWB_LOCK 0x01 + +#define AWB_MODE I2C_REG(CAT_WB, 0x02, 1) +#define REG_AWB_AUTO 0x01 /* AWB off */ +#define REG_AWB_PRESET 0x02 /* AWB preset */ + +/* Manual WB (preset) */ +#define AWB_MANUAL I2C_REG(CAT_WB, 0x03, 1) +#define REG_AWB_INCANDESCENT 0x01 +#define REG_AWB_FLUORESCENT_1 0x02 +#define REG_AWB_FLUORESCENT_2 0x03 +#define REG_AWB_DAYLIGHT 0x04 +#define REG_AWB_CLOUDY 0x05 +#define REG_AWB_SHADE 0x06 +#define REG_AWB_HORIZON 0x07 +#define REG_AWB_LEDLIGHT 0x09 + +/* + * Category 7 - EXIF information + */ +#define EXIF_INFO_EXPTIME_NU I2C_REG(CAT_EXIF, 0x00, 4) +#define EXIF_INFO_EXPTIME_DE I2C_REG(CAT_EXIF, 0x04, 4) +#define EXIF_INFO_TV_NU I2C_REG(CAT_EXIF, 0x08, 4) +#define EXIF_INFO_TV_DE I2C_REG(CAT_EXIF, 0x0c, 4) +#define EXIF_INFO_AV_NU I2C_REG(CAT_EXIF, 0x10, 4) +#define EXIF_INFO_AV_DE I2C_REG(CAT_EXIF, 0x14, 4) +#define EXIF_INFO_BV_NU I2C_REG(CAT_EXIF, 0x18, 4) +#define EXIF_INFO_BV_DE I2C_REG(CAT_EXIF, 0x1c, 4) +#define EXIF_INFO_EBV_NU I2C_REG(CAT_EXIF, 0x20, 4) +#define EXIF_INFO_EBV_DE I2C_REG(CAT_EXIF, 0x24, 4) +#define EXIF_INFO_ISO I2C_REG(CAT_EXIF, 0x28, 2) +#define EXIF_INFO_FLASH I2C_REG(CAT_EXIF, 0x2a, 2) +#define EXIF_INFO_SDR I2C_REG(CAT_EXIF, 0x2c, 2) +#define EXIF_INFO_QVAL I2C_REG(CAT_EXIF, 0x2e, 2) + +/* + * Category 9 - Face Detection + */ +#define FD_CTL I2C_REG(CAT_FD, 0x00, 1) +#define BIT_FD_EN 0 +#define BIT_FD_DRAW_FACE_FRAME 4 +#define BIT_FD_DRAW_SMILE_LVL 6 +#define REG_FD(shift) (1 << shift) +#define REG_FD_OFF 0x0 + +/* + * Category A - Lens Parameter + */ +#define AF_MODE I2C_REG(CAT_LENS, 0x01, 1) +#define REG_AF_NORMAL 0x00 /* Normal AF, one time */ +#define REG_AF_MACRO 0x01 /* Macro AF, one time */ +#define REG_AF_POWEROFF 0x07 + +#define AF_EXECUTE I2C_REG(CAT_LENS, 0x02, 1) +#define REG_AF_STOP 0x00 +#define REG_AF_EXE_AUTO 0x01 +#define REG_AF_EXE_CAF 0x02 + +#define AF_STATUS I2C_REG(CAT_LENS, 0x03, 1) +#define REG_AF_FAIL 0x00 +#define REG_AF_SUCCESS 0x02 +#define REG_AF_IDLE 0x04 +#define REG_AF_BUSY 0x05 + +#define AF_VERSION I2C_REG(CAT_LENS, 0x0a, 1) + +/* + * Category B - CAPTURE Parameter + */ +#define CAPP_YUVOUT_MAIN I2C_REG(CAT_CAPT_PARM, 0x00, 1) +#define REG_YUV422 0x00 +#define REG_BAYER10 0x05 +#define REG_BAYER8 0x06 +#define REG_JPEG 0x10 + +#define CAPP_MAIN_IMAGE_SIZE I2C_REG(CAT_CAPT_PARM, 0x01, 1) +#define CAPP_JPEG_RATIO I2C_REG(CAT_CAPT_PARM, 0x17, 1) + +#define CAPP_MCC_MODE I2C_REG(CAT_CAPT_PARM, 0x1d, 1) +#define REG_MCC_OFF 0x00 +#define REG_MCC_NORMAL 0x01 + +#define CAPP_WDR_EN I2C_REG(CAT_CAPT_PARM, 0x2c, 1) +#define REG_WDR_OFF 0x00 +#define REG_WDR_ON 0x01 +#define REG_WDR_AUTO 0x02 + +#define CAPP_LIGHT_CTRL I2C_REG(CAT_CAPT_PARM, 0x40, 1) +#define REG_LIGHT_OFF 0x00 +#define REG_LIGHT_ON 0x01 +#define REG_LIGHT_AUTO 0x02 + +#define CAPP_FLASH_CTRL I2C_REG(CAT_CAPT_PARM, 0x41, 1) +#define REG_FLASH_OFF 0x00 +#define REG_FLASH_ON 0x01 +#define REG_FLASH_AUTO 0x02 + +/* + * Category C - CAPTURE Control + */ +#define CAPC_MODE I2C_REG(CAT_CAPT_CTRL, 0x00, 1) +#define REG_CAP_NONE 0x00 +#define REG_CAP_ANTI_SHAKE 0x02 + +/* Select single- or multi-shot capture */ +#define CAPC_SEL_FRAME I2C_REG(CAT_CAPT_CTRL, 0x06, 1) + +#define CAPC_START I2C_REG(CAT_CAPT_CTRL, 0x09, 1) +#define REG_CAP_START_MAIN 0x01 +#define REG_CAP_START_THUMB 0x03 + +#define CAPC_IMAGE_SIZE I2C_REG(CAT_CAPT_CTRL, 0x0d, 4) +#define CAPC_THUMB_SIZE I2C_REG(CAT_CAPT_CTRL, 0x11, 4) + +/* + * Category F - Flash + * + * This mode provides functions about internal flash stuff and system startup. + */ + +/* Starts internal ARM core booting after power-up */ +#define FLASH_CAM_START I2C_REG(CAT_FLASH, 0x12, 1) +#define REG_START_ARM_BOOT 0x01 /* write value */ +#define REG_IN_FLASH_MODE 0x00 /* read value */ + +#endif /* M5MOLS_REG_H */ diff --git a/drivers/media/i2c/msp3400-driver.c b/drivers/media/i2c/msp3400-driver.c new file mode 100644 index 00000000000..aeb22be7dcb --- /dev/null +++ b/drivers/media/i2c/msp3400-driver.c @@ -0,0 +1,899 @@ +/* + * Programming the mspx4xx sound processor family + * + * (c) 1997-2001 Gerd Knorr + * + * what works and what doesn't: + * + * AM-Mono + * Support for Hauppauge cards added (decoding handled by tuner) added by + * Frederic Crozat + * + * FM-Mono + * should work. The stereo modes are backward compatible to FM-mono, + * therefore FM-Mono should be allways available. + * + * FM-Stereo (B/G, used in germany) + * should work, with autodetect + * + * FM-Stereo (satellite) + * should work, no autodetect (i.e. default is mono, but you can + * switch to stereo -- untested) + * + * NICAM (B/G, L , used in UK, Scandinavia, Spain and France) + * should work, with autodetect. Support for NICAM was added by + * Pekka Pietikainen + * + * TODO: + * - better SAT support + * + * 980623 Thomas Sailer (sailer@ife.ee.ethz.ch) + * using soundcore instead of OSS + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msp3400-driver.h" + +/* ---------------------------------------------------------------------- */ + +MODULE_DESCRIPTION("device driver for msp34xx TV sound processor"); +MODULE_AUTHOR("Gerd Knorr"); +MODULE_LICENSE("GPL"); + +/* module parameters */ +static int opmode = OPMODE_AUTO; +int msp_debug; /* msp_debug output */ +bool msp_once; /* no continuous stereo monitoring */ +bool msp_amsound; /* hard-wire AM sound at 6.5 Hz (france), + the autoscan seems work well only with FM... */ +int msp_standard = 1; /* Override auto detect of audio msp_standard, + if needed. */ +bool msp_dolby; + +int msp_stereo_thresh = 0x190; /* a2 threshold for stereo/bilingual + (msp34xxg only) 0x00a0-0x03c0 */ + +/* read-only */ +module_param(opmode, int, 0444); + +/* read-write */ +module_param_named(once, msp_once, bool, 0644); +module_param_named(debug, msp_debug, int, 0644); +module_param_named(stereo_threshold, msp_stereo_thresh, int, 0644); +module_param_named(standard, msp_standard, int, 0644); +module_param_named(amsound, msp_amsound, bool, 0644); +module_param_named(dolby, msp_dolby, bool, 0644); + +MODULE_PARM_DESC(opmode, "Forces a MSP3400 opmode. 0=Manual, 1=Autodetect, 2=Autodetect and autoselect"); +MODULE_PARM_DESC(once, "No continuous stereo monitoring"); +MODULE_PARM_DESC(debug, "Enable debug messages [0-3]"); +MODULE_PARM_DESC(stereo_threshold, "Sets signal threshold to activate stereo"); +MODULE_PARM_DESC(standard, "Specify audio standard: 32 = NTSC, 64 = radio, Default: Autodetect"); +MODULE_PARM_DESC(amsound, "Hardwire AM sound at 6.5Hz (France), FM can autoscan"); +MODULE_PARM_DESC(dolby, "Activates Dolby processing"); + +/* ---------------------------------------------------------------------- */ + +/* control subaddress */ +#define I2C_MSP_CONTROL 0x00 +/* demodulator unit subaddress */ +#define I2C_MSP_DEM 0x10 +/* DSP unit subaddress */ +#define I2C_MSP_DSP 0x12 + + +/* ----------------------------------------------------------------------- */ +/* functions for talking to the MSP3400C Sound processor */ + +int msp_reset(struct i2c_client *client) +{ + /* reset and read revision code */ + static u8 reset_off[3] = { I2C_MSP_CONTROL, 0x80, 0x00 }; + static u8 reset_on[3] = { I2C_MSP_CONTROL, 0x00, 0x00 }; + static u8 write[3] = { I2C_MSP_DSP + 1, 0x00, 0x1e }; + u8 read[2]; + struct i2c_msg reset[2] = { + { client->addr, I2C_M_IGNORE_NAK, 3, reset_off }, + { client->addr, I2C_M_IGNORE_NAK, 3, reset_on }, + }; + struct i2c_msg test[2] = { + { client->addr, 0, 3, write }, + { client->addr, I2C_M_RD, 2, read }, + }; + + v4l_dbg(3, msp_debug, client, "msp_reset\n"); + if (i2c_transfer(client->adapter, &reset[0], 1) != 1 || + i2c_transfer(client->adapter, &reset[1], 1) != 1 || + i2c_transfer(client->adapter, test, 2) != 2) { + v4l_err(client, "chip reset failed\n"); + return -1; + } + return 0; +} + +static int msp_read(struct i2c_client *client, int dev, int addr) +{ + int err, retval; + u8 write[3]; + u8 read[2]; + struct i2c_msg msgs[2] = { + { client->addr, 0, 3, write }, + { client->addr, I2C_M_RD, 2, read } + }; + + write[0] = dev + 1; + write[1] = addr >> 8; + write[2] = addr & 0xff; + + for (err = 0; err < 3; err++) { + if (i2c_transfer(client->adapter, msgs, 2) == 2) + break; + v4l_warn(client, "I/O error #%d (read 0x%02x/0x%02x)\n", err, + dev, addr); + schedule_timeout_interruptible(msecs_to_jiffies(10)); + } + if (err == 3) { + v4l_warn(client, "resetting chip, sound will go off.\n"); + msp_reset(client); + return -1; + } + retval = read[0] << 8 | read[1]; + v4l_dbg(3, msp_debug, client, "msp_read(0x%x, 0x%x): 0x%x\n", + dev, addr, retval); + return retval; +} + +int msp_read_dem(struct i2c_client *client, int addr) +{ + return msp_read(client, I2C_MSP_DEM, addr); +} + +int msp_read_dsp(struct i2c_client *client, int addr) +{ + return msp_read(client, I2C_MSP_DSP, addr); +} + +static int msp_write(struct i2c_client *client, int dev, int addr, int val) +{ + int err; + u8 buffer[5]; + + buffer[0] = dev; + buffer[1] = addr >> 8; + buffer[2] = addr & 0xff; + buffer[3] = val >> 8; + buffer[4] = val & 0xff; + + v4l_dbg(3, msp_debug, client, "msp_write(0x%x, 0x%x, 0x%x)\n", + dev, addr, val); + for (err = 0; err < 3; err++) { + if (i2c_master_send(client, buffer, 5) == 5) + break; + v4l_warn(client, "I/O error #%d (write 0x%02x/0x%02x)\n", err, + dev, addr); + schedule_timeout_interruptible(msecs_to_jiffies(10)); + } + if (err == 3) { + v4l_warn(client, "resetting chip, sound will go off.\n"); + msp_reset(client); + return -1; + } + return 0; +} + +int msp_write_dem(struct i2c_client *client, int addr, int val) +{ + return msp_write(client, I2C_MSP_DEM, addr, val); +} + +int msp_write_dsp(struct i2c_client *client, int addr, int val) +{ + return msp_write(client, I2C_MSP_DSP, addr, val); +} + +/* ----------------------------------------------------------------------- * + * bits 9 8 5 - SCART DSP input Select: + * 0 0 0 - SCART 1 to DSP input (reset position) + * 0 1 0 - MONO to DSP input + * 1 0 0 - SCART 2 to DSP input + * 1 1 1 - Mute DSP input + * + * bits 11 10 6 - SCART 1 Output Select: + * 0 0 0 - undefined (reset position) + * 0 1 0 - SCART 2 Input to SCART 1 Output (for devices with 2 SCARTS) + * 1 0 0 - MONO input to SCART 1 Output + * 1 1 0 - SCART 1 DA to SCART 1 Output + * 0 0 1 - SCART 2 DA to SCART 1 Output + * 0 1 1 - SCART 1 Input to SCART 1 Output + * 1 1 1 - Mute SCART 1 Output + * + * bits 13 12 7 - SCART 2 Output Select (for devices with 2 Output SCART): + * 0 0 0 - SCART 1 DA to SCART 2 Output (reset position) + * 0 1 0 - SCART 1 Input to SCART 2 Output + * 1 0 0 - MONO input to SCART 2 Output + * 0 0 1 - SCART 2 DA to SCART 2 Output + * 0 1 1 - SCART 2 Input to SCART 2 Output + * 1 1 0 - Mute SCART 2 Output + * + * Bits 4 to 0 should be zero. + * ----------------------------------------------------------------------- */ + +static int scarts[3][9] = { + /* MASK IN1 IN2 IN3 IN4 IN1_DA IN2_DA MONO MUTE */ + /* SCART DSP Input select */ + { 0x0320, 0x0000, 0x0200, 0x0300, 0x0020, -1, -1, 0x0100, 0x0320 }, + /* SCART1 Output select */ + { 0x0c40, 0x0440, 0x0400, 0x0000, 0x0840, 0x0c00, 0x0040, 0x0800, 0x0c40 }, + /* SCART2 Output select */ + { 0x3080, 0x1000, 0x1080, 0x2080, 0x3080, 0x0000, 0x0080, 0x2000, 0x3000 }, +}; + +static char *scart_names[] = { + "in1", "in2", "in3", "in4", "in1 da", "in2 da", "mono", "mute" +}; + +void msp_set_scart(struct i2c_client *client, int in, int out) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + + state->in_scart = in; + + if (in >= 0 && in <= 7 && out >= 0 && out <= 2) { + if (-1 == scarts[out][in + 1]) + return; + + state->acb &= ~scarts[out][0]; + state->acb |= scarts[out][in + 1]; + } else + state->acb = 0xf60; /* Mute Input and SCART 1 Output */ + + v4l_dbg(1, msp_debug, client, "scart switch: %s => %d (ACB=0x%04x)\n", + scart_names[in], out, state->acb); + msp_write_dsp(client, 0x13, state->acb); + + /* Sets I2S speed 0 = 1.024 Mbps, 1 = 2.048 Mbps */ + if (state->has_i2s_conf) + msp_write_dem(client, 0x40, state->i2s_mode); +} + +/* ------------------------------------------------------------------------ */ + +static void msp_wake_thread(struct i2c_client *client) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + + if (NULL == state->kthread) + return; + state->watch_stereo = 0; + state->restart = 1; + wake_up_interruptible(&state->wq); +} + +int msp_sleep(struct msp_state *state, int timeout) +{ + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&state->wq, &wait); + if (!kthread_should_stop()) { + if (timeout < 0) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } else { + schedule_timeout_interruptible + (msecs_to_jiffies(timeout)); + } + } + + remove_wait_queue(&state->wq, &wait); + try_to_freeze(); + return state->restart; +} + +/* ------------------------------------------------------------------------ */ + +static int msp_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct msp_state *state = ctrl_to_state(ctrl); + struct i2c_client *client = v4l2_get_subdevdata(&state->sd); + int val = ctrl->val; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: { + /* audio volume cluster */ + int reallymuted = state->muted->val | state->scan_in_progress; + + if (!reallymuted) + val = (val * 0x7f / 65535) << 8; + + v4l_dbg(1, msp_debug, client, "mute=%s scanning=%s volume=%d\n", + state->muted->val ? "on" : "off", + state->scan_in_progress ? "yes" : "no", + state->volume->val); + + msp_write_dsp(client, 0x0000, val); + msp_write_dsp(client, 0x0007, reallymuted ? 0x1 : (val | 0x1)); + if (state->has_scart2_out_volume) + msp_write_dsp(client, 0x0040, reallymuted ? 0x1 : (val | 0x1)); + if (state->has_headphones) + msp_write_dsp(client, 0x0006, val); + break; + } + + case V4L2_CID_AUDIO_BASS: + val = ((val - 32768) * 0x60 / 65535) << 8; + msp_write_dsp(client, 0x0002, val); + if (state->has_headphones) + msp_write_dsp(client, 0x0031, val); + break; + + case V4L2_CID_AUDIO_TREBLE: + val = ((val - 32768) * 0x60 / 65535) << 8; + msp_write_dsp(client, 0x0003, val); + if (state->has_headphones) + msp_write_dsp(client, 0x0032, val); + break; + + case V4L2_CID_AUDIO_LOUDNESS: + val = val ? ((5 * 4) << 8) : 0; + msp_write_dsp(client, 0x0004, val); + if (state->has_headphones) + msp_write_dsp(client, 0x0033, val); + break; + + case V4L2_CID_AUDIO_BALANCE: + val = (u8)((val / 256) - 128); + msp_write_dsp(client, 0x0001, val << 8); + if (state->has_headphones) + msp_write_dsp(client, 0x0030, val << 8); + break; + + default: + return -EINVAL; + } + return 0; +} + +void msp_update_volume(struct msp_state *state) +{ + /* Force an update of the volume/mute cluster */ + v4l2_ctrl_lock(state->volume); + state->volume->val = state->volume->cur.val; + state->muted->val = state->muted->cur.val; + msp_s_ctrl(state->volume); + v4l2_ctrl_unlock(state->volume); +} + +/* --- v4l2 ioctls --- */ +static int msp_s_radio(struct v4l2_subdev *sd) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (state->radio) + return 0; + state->radio = 1; + v4l_dbg(1, msp_debug, client, "switching to radio mode\n"); + state->watch_stereo = 0; + switch (state->opmode) { + case OPMODE_MANUAL: + /* set msp3400 to FM radio mode */ + msp3400c_set_mode(client, MSP_MODE_FM_RADIO); + msp3400c_set_carrier(client, MSP_CARRIER(10.7), + MSP_CARRIER(10.7)); + msp_update_volume(state); + break; + case OPMODE_AUTODETECT: + case OPMODE_AUTOSELECT: + /* the thread will do for us */ + msp_wake_thread(client); + break; + } + return 0; +} + +static int msp_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + /* new channel -- kick audio carrier scan */ + msp_wake_thread(client); + return 0; +} + +static int msp_querystd(struct v4l2_subdev *sd, v4l2_std_id *id) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + *id &= state->detected_std; + + v4l_dbg(2, msp_debug, client, + "detected standard: %s(0x%08Lx)\n", + msp_standard_std_name(state->std), state->detected_std); + + return 0; +} + +static int msp_s_std(struct v4l2_subdev *sd, v4l2_std_id id) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int update = state->radio || state->v4l2_std != id; + + state->v4l2_std = id; + state->radio = 0; + if (update) + msp_wake_thread(client); + return 0; +} + +static int msp_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int tuner = (input >> 3) & 1; + int sc_in = input & 0x7; + int sc1_out = output & 0xf; + int sc2_out = (output >> 4) & 0xf; + u16 val, reg; + int i; + int extern_input = 1; + + if (state->route_in == input && state->route_out == output) + return 0; + state->route_in = input; + state->route_out = output; + /* check if the tuner input is used */ + for (i = 0; i < 5; i++) { + if (((input >> (4 + i * 4)) & 0xf) == 0) + extern_input = 0; + } + state->mode = extern_input ? MSP_MODE_EXTERN : MSP_MODE_AM_DETECT; + state->rxsubchans = V4L2_TUNER_SUB_STEREO; + msp_set_scart(client, sc_in, 0); + msp_set_scart(client, sc1_out, 1); + msp_set_scart(client, sc2_out, 2); + msp_set_audmode(client); + reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb; + val = msp_read_dem(client, reg); + msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8)); + /* wake thread when a new input is chosen */ + msp_wake_thread(client); + return 0; +} + +static int msp_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (vt->type != V4L2_TUNER_ANALOG_TV) + return 0; + if (!state->radio) { + if (state->opmode == OPMODE_AUTOSELECT) + msp_detect_stereo(client); + vt->rxsubchans = state->rxsubchans; + } + vt->audmode = state->audmode; + vt->capability |= V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; + return 0; +} + +static int msp_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (state->radio) /* TODO: add mono/stereo support for radio */ + return 0; + if (state->audmode == vt->audmode) + return 0; + state->audmode = vt->audmode; + /* only set audmode */ + msp_set_audmode(client); + return 0; +} + +static int msp_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + v4l_dbg(1, msp_debug, client, "Setting I2S speed to %d\n", freq); + + switch (freq) { + case 1024000: + state->i2s_mode = 0; + break; + case 2048000: + state->i2s_mode = 1; + break; + default: + return -EINVAL; + } + return 0; +} + +static int msp_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, state->ident, + (state->rev1 << 16) | state->rev2); +} + +static int msp_log_status(struct v4l2_subdev *sd) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + const char *p; + char prefix[V4L2_SUBDEV_NAME_SIZE + 20]; + + if (state->opmode == OPMODE_AUTOSELECT) + msp_detect_stereo(client); + v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n", + client->name, state->rev1, state->rev2); + snprintf(prefix, sizeof(prefix), "%s: Audio: ", sd->name); + v4l2_ctrl_handler_log_status(&state->hdl, prefix); + switch (state->mode) { + case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break; + case MSP_MODE_FM_RADIO: p = "FM Radio"; break; + case MSP_MODE_FM_TERRA: p = "Terrestrial FM-mono/stereo"; break; + case MSP_MODE_FM_SAT: p = "Satellite FM-mono"; break; + case MSP_MODE_FM_NICAM1: p = "NICAM/FM (B/G, D/K)"; break; + case MSP_MODE_FM_NICAM2: p = "NICAM/FM (I)"; break; + case MSP_MODE_AM_NICAM: p = "NICAM/AM (L)"; break; + case MSP_MODE_BTSC: p = "BTSC"; break; + case MSP_MODE_EXTERN: p = "External input"; break; + default: p = "unknown"; break; + } + if (state->mode == MSP_MODE_EXTERN) { + v4l_info(client, "Mode: %s\n", p); + } else if (state->opmode == OPMODE_MANUAL) { + v4l_info(client, "Mode: %s (%s%s)\n", p, + (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono", + (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : ""); + } else { + if (state->opmode == OPMODE_AUTODETECT) + v4l_info(client, "Mode: %s\n", p); + v4l_info(client, "Standard: %s (%s%s)\n", + msp_standard_std_name(state->std), + (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono", + (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : ""); + } + v4l_info(client, "Audmode: 0x%04x\n", state->audmode); + v4l_info(client, "Routing: 0x%08x (input) 0x%08x (output)\n", + state->route_in, state->route_out); + v4l_info(client, "ACB: 0x%04x\n", state->acb); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int msp_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + v4l_dbg(1, msp_debug, client, "suspend\n"); + msp_reset(client); + return 0; +} + +static int msp_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + v4l_dbg(1, msp_debug, client, "resume\n"); + msp_wake_thread(client); + return 0; +} +#endif + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops msp_ctrl_ops = { + .s_ctrl = msp_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops msp_core_ops = { + .log_status = msp_log_status, + .g_chip_ident = msp_g_chip_ident, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, + .s_std = msp_s_std, +}; + +static const struct v4l2_subdev_video_ops msp_video_ops = { + .querystd = msp_querystd, +}; + +static const struct v4l2_subdev_tuner_ops msp_tuner_ops = { + .s_frequency = msp_s_frequency, + .g_tuner = msp_g_tuner, + .s_tuner = msp_s_tuner, + .s_radio = msp_s_radio, +}; + +static const struct v4l2_subdev_audio_ops msp_audio_ops = { + .s_routing = msp_s_routing, + .s_i2s_clock_freq = msp_s_i2s_clock_freq, +}; + +static const struct v4l2_subdev_ops msp_ops = { + .core = &msp_core_ops, + .video = &msp_video_ops, + .tuner = &msp_tuner_ops, + .audio = &msp_audio_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct msp_state *state; + struct v4l2_subdev *sd; + struct v4l2_ctrl_handler *hdl; + int (*thread_func)(void *data) = NULL; + int msp_hard; + int msp_family; + int msp_revision; + int msp_product, msp_prod_hi, msp_prod_lo; + int msp_rom; + + if (!id) + strlcpy(client->name, "msp3400", sizeof(client->name)); + + if (msp_reset(client) == -1) { + v4l_dbg(1, msp_debug, client, "msp3400 not found\n"); + return -ENODEV; + } + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &msp_ops); + + state->v4l2_std = V4L2_STD_NTSC; + state->detected_std = V4L2_STD_ALL; + state->audmode = V4L2_TUNER_MODE_STEREO; + state->input = -1; + state->i2s_mode = 0; + init_waitqueue_head(&state->wq); + /* These are the reset input/output positions */ + state->route_in = MSP_INPUT_DEFAULT; + state->route_out = MSP_OUTPUT_DEFAULT; + + state->rev1 = msp_read_dsp(client, 0x1e); + if (state->rev1 != -1) + state->rev2 = msp_read_dsp(client, 0x1f); + v4l_dbg(1, msp_debug, client, "rev1=0x%04x, rev2=0x%04x\n", + state->rev1, state->rev2); + if (state->rev1 == -1 || (state->rev1 == 0 && state->rev2 == 0)) { + v4l_dbg(1, msp_debug, client, + "not an msp3400 (cannot read chip version)\n"); + kfree(state); + return -ENODEV; + } + + msp_family = ((state->rev1 >> 4) & 0x0f) + 3; + msp_product = (state->rev2 >> 8) & 0xff; + msp_prod_hi = msp_product / 10; + msp_prod_lo = msp_product % 10; + msp_revision = (state->rev1 & 0x0f) + '@'; + msp_hard = ((state->rev1 >> 8) & 0xff) + '@'; + msp_rom = state->rev2 & 0x1f; + /* Rev B=2, C=3, D=4, G=7 */ + state->ident = msp_family * 10000 + 4000 + msp_product * 10 + + msp_revision - '@'; + + /* Has NICAM support: all mspx41x and mspx45x products have NICAM */ + state->has_nicam = + msp_prod_hi == 1 || msp_prod_hi == 5; + /* Has radio support: was added with revision G */ + state->has_radio = + msp_revision >= 'G'; + /* Has headphones output: not for stripped down products */ + state->has_headphones = + msp_prod_lo < 5; + /* Has scart2 input: not in stripped down products of the '3' family */ + state->has_scart2 = + msp_family >= 4 || msp_prod_lo < 7; + /* Has scart3 input: not in stripped down products of the '3' family */ + state->has_scart3 = + msp_family >= 4 || msp_prod_lo < 5; + /* Has scart4 input: not in pre D revisions, not in stripped D revs */ + state->has_scart4 = + msp_family >= 4 || (msp_revision >= 'D' && msp_prod_lo < 5); + /* Has scart2 output: not in stripped down products of + * the '3' family */ + state->has_scart2_out = + msp_family >= 4 || msp_prod_lo < 5; + /* Has scart2 a volume control? Not in pre-D revisions. */ + state->has_scart2_out_volume = + msp_revision > 'C' && state->has_scart2_out; + /* Has a configurable i2s out? */ + state->has_i2s_conf = + msp_revision >= 'G' && msp_prod_lo < 7; + /* Has subwoofer output: not in pre-D revs and not in stripped down + * products */ + state->has_subwoofer = + msp_revision >= 'D' && msp_prod_lo < 5; + /* Has soundprocessing (bass/treble/balance/loudness/equalizer): + * not in stripped down products */ + state->has_sound_processing = + msp_prod_lo < 7; + /* Has Virtual Dolby Surround: only in msp34x1 */ + state->has_virtual_dolby_surround = + msp_revision == 'G' && msp_prod_lo == 1; + /* Has Virtual Dolby Surround & Dolby Pro Logic: only in msp34x2 */ + state->has_dolby_pro_logic = + msp_revision == 'G' && msp_prod_lo == 2; + /* The msp343xG supports BTSC only and cannot do Automatic Standard + * Detection. */ + state->force_btsc = + msp_family == 3 && msp_revision == 'G' && msp_prod_hi == 3; + + state->opmode = opmode; + if (state->opmode == OPMODE_AUTO) { + /* MSP revision G and up have both autodetect and autoselect */ + if (msp_revision >= 'G') + state->opmode = OPMODE_AUTOSELECT; + /* MSP revision D and up have autodetect */ + else if (msp_revision >= 'D') + state->opmode = OPMODE_AUTODETECT; + else + state->opmode = OPMODE_MANUAL; + } + + hdl = &state->hdl; + v4l2_ctrl_handler_init(hdl, 6); + if (state->has_sound_processing) { + v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, + V4L2_CID_AUDIO_BASS, 0, 65535, 65535 / 100, 32768); + v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, + V4L2_CID_AUDIO_TREBLE, 0, 65535, 65535 / 100, 32768); + v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, + V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 0); + } + state->volume = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 58880); + v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, + V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768); + state->muted = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + sd->ctrl_handler = hdl; + if (hdl->error) { + int err = hdl->error; + + v4l2_ctrl_handler_free(hdl); + kfree(state); + return err; + } + + v4l2_ctrl_cluster(2, &state->volume); + v4l2_ctrl_handler_setup(hdl); + + /* hello world :-) */ + v4l_info(client, "MSP%d4%02d%c-%c%d found @ 0x%x (%s)\n", + msp_family, msp_product, + msp_revision, msp_hard, msp_rom, + client->addr << 1, client->adapter->name); + v4l_info(client, "%s ", client->name); + if (state->has_nicam && state->has_radio) + printk(KERN_CONT "supports nicam and radio, "); + else if (state->has_nicam) + printk(KERN_CONT "supports nicam, "); + else if (state->has_radio) + printk(KERN_CONT "supports radio, "); + printk(KERN_CONT "mode is "); + + /* version-specific initialization */ + switch (state->opmode) { + case OPMODE_MANUAL: + printk(KERN_CONT "manual"); + thread_func = msp3400c_thread; + break; + case OPMODE_AUTODETECT: + printk(KERN_CONT "autodetect"); + thread_func = msp3410d_thread; + break; + case OPMODE_AUTOSELECT: + printk(KERN_CONT "autodetect and autoselect"); + thread_func = msp34xxg_thread; + break; + } + printk(KERN_CONT "\n"); + + /* startup control thread if needed */ + if (thread_func) { + state->kthread = kthread_run(thread_func, client, "msp34xx"); + + if (IS_ERR(state->kthread)) + v4l_warn(client, "kernel_thread() failed\n"); + msp_wake_thread(client); + } + return 0; +} + +static int msp_remove(struct i2c_client *client) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + + v4l2_device_unregister_subdev(&state->sd); + /* shutdown control thread */ + if (state->kthread) { + state->restart = 1; + kthread_stop(state->kthread); + } + msp_reset(client); + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct dev_pm_ops msp3400_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(msp_suspend, msp_resume) +}; + +static const struct i2c_device_id msp_id[] = { + { "msp3400", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, msp_id); + +static struct i2c_driver msp_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "msp3400", + .pm = &msp3400_pm_ops, + }, + .probe = msp_probe, + .remove = msp_remove, + .id_table = msp_id, +}; + +module_i2c_driver(msp_driver); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/i2c/msp3400-driver.h b/drivers/media/i2c/msp3400-driver.h new file mode 100644 index 00000000000..fbe5e0715f9 --- /dev/null +++ b/drivers/media/i2c/msp3400-driver.h @@ -0,0 +1,137 @@ +/* + */ + +#ifndef MSP3400_DRIVER_H +#define MSP3400_DRIVER_H + +#include +#include +#include + +/* ---------------------------------------------------------------------- */ + +/* This macro is allowed for *constants* only, gcc must calculate it + at compile time. Remember -- no floats in kernel mode */ +#define MSP_CARRIER(freq) ((int)((float)(freq / 18.432) * (1 << 24))) + +#define MSP_MODE_AM_DETECT 0 +#define MSP_MODE_FM_RADIO 2 +#define MSP_MODE_FM_TERRA 3 +#define MSP_MODE_FM_SAT 4 +#define MSP_MODE_FM_NICAM1 5 +#define MSP_MODE_FM_NICAM2 6 +#define MSP_MODE_AM_NICAM 7 +#define MSP_MODE_BTSC 8 +#define MSP_MODE_EXTERN 9 + +#define SCART_IN1 0 +#define SCART_IN2 1 +#define SCART_IN3 2 +#define SCART_IN4 3 +#define SCART_IN1_DA 4 +#define SCART_IN2_DA 5 +#define SCART_MONO 6 +#define SCART_MUTE 7 + +#define SCART_DSP_IN 0 +#define SCART1_OUT 1 +#define SCART2_OUT 2 + +#define OPMODE_AUTO -1 +#define OPMODE_MANUAL 0 +#define OPMODE_AUTODETECT 1 /* use autodetect (>= msp3410 only) */ +#define OPMODE_AUTOSELECT 2 /* use autodetect & autoselect (>= msp34xxG) */ + +/* module parameters */ +extern int msp_debug; +extern bool msp_once; +extern bool msp_amsound; +extern int msp_standard; +extern bool msp_dolby; +extern int msp_stereo_thresh; + +struct msp_state { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + int rev1, rev2; + int ident; + u8 has_nicam; + u8 has_radio; + u8 has_headphones; + u8 has_ntsc_jp_d_k3; + u8 has_scart2; + u8 has_scart3; + u8 has_scart4; + u8 has_scart2_out; + u8 has_scart2_out_volume; + u8 has_i2s_conf; + u8 has_subwoofer; + u8 has_sound_processing; + u8 has_virtual_dolby_surround; + u8 has_dolby_pro_logic; + u8 force_btsc; + + int radio; + int opmode; + int std; + int mode; + v4l2_std_id v4l2_std, detected_std; + int nicam_on; + int acb; + int in_scart; + int i2s_mode; + int main, second; /* sound carrier */ + int input; + u32 route_in; + u32 route_out; + + /* v4l2 */ + int audmode; + int rxsubchans; + + struct { + /* volume cluster */ + struct v4l2_ctrl *volume; + struct v4l2_ctrl *muted; + }; + + int scan_in_progress; + + /* thread */ + struct task_struct *kthread; + wait_queue_head_t wq; + unsigned int restart:1; + unsigned int watch_stereo:1; +}; + +static inline struct msp_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct msp_state, sd); +} + +static inline struct msp_state *ctrl_to_state(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct msp_state, hdl); +} + +/* msp3400-driver.c */ +int msp_write_dem(struct i2c_client *client, int addr, int val); +int msp_write_dsp(struct i2c_client *client, int addr, int val); +int msp_read_dem(struct i2c_client *client, int addr); +int msp_read_dsp(struct i2c_client *client, int addr); +int msp_reset(struct i2c_client *client); +void msp_set_scart(struct i2c_client *client, int in, int out); +void msp_update_volume(struct msp_state *state); +int msp_sleep(struct msp_state *state, int timeout); + +/* msp3400-kthreads.c */ +const char *msp_standard_std_name(int std); +void msp_set_audmode(struct i2c_client *client); +int msp_detect_stereo(struct i2c_client *client); +int msp3400c_thread(void *data); +int msp3410d_thread(void *data); +int msp34xxg_thread(void *data); +void msp3400c_set_mode(struct i2c_client *client, int mode); +void msp3400c_set_carrier(struct i2c_client *client, int cdo1, int cdo2); + +#endif /* MSP3400_DRIVER_H */ diff --git a/drivers/media/i2c/msp3400-kthreads.c b/drivers/media/i2c/msp3400-kthreads.c new file mode 100644 index 00000000000..f8b51714f2f --- /dev/null +++ b/drivers/media/i2c/msp3400-kthreads.c @@ -0,0 +1,1165 @@ +/* + * Programming the mspx4xx sound processor family + * + * (c) 1997-2001 Gerd Knorr + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msp3400-driver.h" + +/* this one uses the automatic sound standard detection of newer msp34xx + chip versions */ +static struct { + int retval; + int main, second; + char *name; + v4l2_std_id std; +} msp_stdlist[] = { + { 0x0000, 0, 0, "could not detect sound standard", V4L2_STD_ALL }, + { 0x0001, 0, 0, "autodetect start", V4L2_STD_ALL }, + { 0x0002, MSP_CARRIER(4.5), MSP_CARRIER(4.72), + "4.5/4.72 M Dual FM-Stereo", V4L2_STD_MN }, + { 0x0003, MSP_CARRIER(5.5), MSP_CARRIER(5.7421875), + "5.5/5.74 B/G Dual FM-Stereo", V4L2_STD_BG }, + { 0x0004, MSP_CARRIER(6.5), MSP_CARRIER(6.2578125), + "6.5/6.25 D/K1 Dual FM-Stereo", V4L2_STD_DK }, + { 0x0005, MSP_CARRIER(6.5), MSP_CARRIER(6.7421875), + "6.5/6.74 D/K2 Dual FM-Stereo", V4L2_STD_DK }, + { 0x0006, MSP_CARRIER(6.5), MSP_CARRIER(6.5), + "6.5 D/K FM-Mono (HDEV3)", V4L2_STD_DK }, + { 0x0007, MSP_CARRIER(6.5), MSP_CARRIER(5.7421875), + "6.5/5.74 D/K3 Dual FM-Stereo", V4L2_STD_DK }, + { 0x0008, MSP_CARRIER(5.5), MSP_CARRIER(5.85), + "5.5/5.85 B/G NICAM FM", V4L2_STD_BG }, + { 0x0009, MSP_CARRIER(6.5), MSP_CARRIER(5.85), + "6.5/5.85 L NICAM AM", V4L2_STD_L }, + { 0x000a, MSP_CARRIER(6.0), MSP_CARRIER(6.55), + "6.0/6.55 I NICAM FM", V4L2_STD_PAL_I }, + { 0x000b, MSP_CARRIER(6.5), MSP_CARRIER(5.85), + "6.5/5.85 D/K NICAM FM", V4L2_STD_DK }, + { 0x000c, MSP_CARRIER(6.5), MSP_CARRIER(5.85), + "6.5/5.85 D/K NICAM FM (HDEV2)", V4L2_STD_DK }, + { 0x000d, MSP_CARRIER(6.5), MSP_CARRIER(5.85), + "6.5/5.85 D/K NICAM FM (HDEV3)", V4L2_STD_DK }, + { 0x0020, MSP_CARRIER(4.5), MSP_CARRIER(4.5), + "4.5 M BTSC-Stereo", V4L2_STD_MTS }, + { 0x0021, MSP_CARRIER(4.5), MSP_CARRIER(4.5), + "4.5 M BTSC-Mono + SAP", V4L2_STD_MTS }, + { 0x0030, MSP_CARRIER(4.5), MSP_CARRIER(4.5), + "4.5 M EIA-J Japan Stereo", V4L2_STD_NTSC_M_JP }, + { 0x0040, MSP_CARRIER(10.7), MSP_CARRIER(10.7), + "10.7 FM-Stereo Radio", V4L2_STD_ALL }, + { 0x0050, MSP_CARRIER(6.5), MSP_CARRIER(6.5), + "6.5 SAT-Mono", V4L2_STD_ALL }, + { 0x0051, MSP_CARRIER(7.02), MSP_CARRIER(7.20), + "7.02/7.20 SAT-Stereo", V4L2_STD_ALL }, + { 0x0060, MSP_CARRIER(7.2), MSP_CARRIER(7.2), + "7.2 SAT ADR", V4L2_STD_ALL }, + { -1, 0, 0, NULL, 0 }, /* EOF */ +}; + +static struct msp3400c_init_data_dem { + int fir1[6]; + int fir2[6]; + int cdo1; + int cdo2; + int ad_cv; + int mode_reg; + int dsp_src; + int dsp_matrix; +} msp3400c_init_data[] = { + { /* AM (for carrier detect / msp3400) */ + {75, 19, 36, 35, 39, 40}, + {75, 19, 36, 35, 39, 40}, + MSP_CARRIER(5.5), MSP_CARRIER(5.5), + 0x00d0, 0x0500, 0x0020, 0x3000 + }, { /* AM (for carrier detect / msp3410) */ + {-1, -1, -8, 2, 59, 126}, + {-1, -1, -8, 2, 59, 126}, + MSP_CARRIER(5.5), MSP_CARRIER(5.5), + 0x00d0, 0x0100, 0x0020, 0x3000 + }, { /* FM Radio */ + {-8, -8, 4, 6, 78, 107}, + {-8, -8, 4, 6, 78, 107}, + MSP_CARRIER(10.7), MSP_CARRIER(10.7), + 0x00d0, 0x0480, 0x0020, 0x3000 + }, { /* Terrestrial FM-mono + FM-stereo */ + {3, 18, 27, 48, 66, 72}, + {3, 18, 27, 48, 66, 72}, + MSP_CARRIER(5.5), MSP_CARRIER(5.5), + 0x00d0, 0x0480, 0x0030, 0x3000 + }, { /* Sat FM-mono */ + { 1, 9, 14, 24, 33, 37}, + { 3, 18, 27, 48, 66, 72}, + MSP_CARRIER(6.5), MSP_CARRIER(6.5), + 0x00c6, 0x0480, 0x0000, 0x3000 + }, { /* NICAM/FM -- B/G (5.5/5.85), D/K (6.5/5.85) */ + {-2, -8, -10, 10, 50, 86}, + {3, 18, 27, 48, 66, 72}, + MSP_CARRIER(5.5), MSP_CARRIER(5.5), + 0x00d0, 0x0040, 0x0120, 0x3000 + }, { /* NICAM/FM -- I (6.0/6.552) */ + {2, 4, -6, -4, 40, 94}, + {3, 18, 27, 48, 66, 72}, + MSP_CARRIER(6.0), MSP_CARRIER(6.0), + 0x00d0, 0x0040, 0x0120, 0x3000 + }, { /* NICAM/AM -- L (6.5/5.85) */ + {-2, -8, -10, 10, 50, 86}, + {-4, -12, -9, 23, 79, 126}, + MSP_CARRIER(6.5), MSP_CARRIER(6.5), + 0x00c6, 0x0140, 0x0120, 0x7c00 + }, +}; + +struct msp3400c_carrier_detect { + int cdo; + char *name; +}; + +static struct msp3400c_carrier_detect msp3400c_carrier_detect_main[] = { + /* main carrier */ + { MSP_CARRIER(4.5), "4.5 NTSC" }, + { MSP_CARRIER(5.5), "5.5 PAL B/G" }, + { MSP_CARRIER(6.0), "6.0 PAL I" }, + { MSP_CARRIER(6.5), "6.5 PAL D/K + SAT + SECAM" } +}; + +static struct msp3400c_carrier_detect msp3400c_carrier_detect_55[] = { + /* PAL B/G */ + { MSP_CARRIER(5.7421875), "5.742 PAL B/G FM-stereo" }, + { MSP_CARRIER(5.85), "5.85 PAL B/G NICAM" } +}; + +static struct msp3400c_carrier_detect msp3400c_carrier_detect_65[] = { + /* PAL SAT / SECAM */ + { MSP_CARRIER(5.85), "5.85 PAL D/K + SECAM NICAM" }, + { MSP_CARRIER(6.2578125), "6.25 PAL D/K1 FM-stereo" }, + { MSP_CARRIER(6.7421875), "6.74 PAL D/K2 FM-stereo" }, + { MSP_CARRIER(7.02), "7.02 PAL SAT FM-stereo s/b" }, + { MSP_CARRIER(7.20), "7.20 PAL SAT FM-stereo s" }, + { MSP_CARRIER(7.38), "7.38 PAL SAT FM-stereo b" }, +}; + +/* ------------------------------------------------------------------------ */ + +const char *msp_standard_std_name(int std) +{ + int i; + + for (i = 0; msp_stdlist[i].name != NULL; i++) + if (msp_stdlist[i].retval == std) + return msp_stdlist[i].name; + return "unknown"; +} + +static v4l2_std_id msp_standard_std(int std) +{ + int i; + + for (i = 0; msp_stdlist[i].name != NULL; i++) + if (msp_stdlist[i].retval == std) + return msp_stdlist[i].std; + return V4L2_STD_ALL; +} + +static void msp_set_source(struct i2c_client *client, u16 src) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + + if (msp_dolby) { + msp_write_dsp(client, 0x0008, 0x0520); /* I2S1 */ + msp_write_dsp(client, 0x0009, 0x0620); /* I2S2 */ + } else { + msp_write_dsp(client, 0x0008, src); + msp_write_dsp(client, 0x0009, src); + } + msp_write_dsp(client, 0x000a, src); + msp_write_dsp(client, 0x000b, src); + msp_write_dsp(client, 0x000c, src); + if (state->has_scart2_out) + msp_write_dsp(client, 0x0041, src); +} + +void msp3400c_set_carrier(struct i2c_client *client, int cdo1, int cdo2) +{ + msp_write_dem(client, 0x0093, cdo1 & 0xfff); + msp_write_dem(client, 0x009b, cdo1 >> 12); + msp_write_dem(client, 0x00a3, cdo2 & 0xfff); + msp_write_dem(client, 0x00ab, cdo2 >> 12); + msp_write_dem(client, 0x0056, 0); /* LOAD_REG_1/2 */ +} + +void msp3400c_set_mode(struct i2c_client *client, int mode) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + struct msp3400c_init_data_dem *data = &msp3400c_init_data[mode]; + int tuner = (state->route_in >> 3) & 1; + int i; + + v4l_dbg(1, msp_debug, client, "set_mode: %d\n", mode); + state->mode = mode; + state->rxsubchans = V4L2_TUNER_SUB_MONO; + + msp_write_dem(client, 0x00bb, data->ad_cv | (tuner ? 0x100 : 0)); + + for (i = 5; i >= 0; i--) /* fir 1 */ + msp_write_dem(client, 0x0001, data->fir1[i]); + + msp_write_dem(client, 0x0005, 0x0004); /* fir 2 */ + msp_write_dem(client, 0x0005, 0x0040); + msp_write_dem(client, 0x0005, 0x0000); + for (i = 5; i >= 0; i--) + msp_write_dem(client, 0x0005, data->fir2[i]); + + msp_write_dem(client, 0x0083, data->mode_reg); + + msp3400c_set_carrier(client, data->cdo1, data->cdo2); + + msp_set_source(client, data->dsp_src); + /* set prescales */ + + /* volume prescale for SCART (AM mono input) */ + msp_write_dsp(client, 0x000d, 0x1900); + msp_write_dsp(client, 0x000e, data->dsp_matrix); + if (state->has_nicam) /* nicam prescale */ + msp_write_dsp(client, 0x0010, 0x5a00); +} + +/* Set audio mode. Note that the pre-'G' models do not support BTSC+SAP, + nor do they support stereo BTSC. */ +static void msp3400c_set_audmode(struct i2c_client *client) +{ + static char *strmode[] = { + "mono", "stereo", "lang2", "lang1", "lang1+lang2" + }; + struct msp_state *state = to_state(i2c_get_clientdata(client)); + char *modestr = (state->audmode >= 0 && state->audmode < 5) ? + strmode[state->audmode] : "unknown"; + int src = 0; /* channel source: FM/AM, nicam or SCART */ + int audmode = state->audmode; + + if (state->opmode == OPMODE_AUTOSELECT) { + /* this method would break everything, let's make sure + * it's never called + */ + v4l_dbg(1, msp_debug, client, + "set_audmode called with mode=%d instead of set_source (ignored)\n", + state->audmode); + return; + } + + /* Note: for the C and D revs no NTSC stereo + SAP is possible as + the hardware does not support SAP. So the rxsubchans combination + of STEREO | LANG2 does not occur. */ + + if (state->mode != MSP_MODE_EXTERN) { + /* switch to mono if only mono is available */ + if (state->rxsubchans == V4L2_TUNER_SUB_MONO) + audmode = V4L2_TUNER_MODE_MONO; + /* if bilingual */ + else if (state->rxsubchans & V4L2_TUNER_SUB_LANG2) { + /* and mono or stereo, then fallback to lang1 */ + if (audmode == V4L2_TUNER_MODE_MONO || + audmode == V4L2_TUNER_MODE_STEREO) + audmode = V4L2_TUNER_MODE_LANG1; + } + /* if stereo, and audmode is not mono, then switch to stereo */ + else if (audmode != V4L2_TUNER_MODE_MONO) + audmode = V4L2_TUNER_MODE_STEREO; + } + + /* switch demodulator */ + switch (state->mode) { + case MSP_MODE_FM_TERRA: + v4l_dbg(1, msp_debug, client, "FM set_audmode: %s\n", modestr); + switch (audmode) { + case V4L2_TUNER_MODE_STEREO: + msp_write_dsp(client, 0x000e, 0x3001); + break; + case V4L2_TUNER_MODE_MONO: + case V4L2_TUNER_MODE_LANG1: + case V4L2_TUNER_MODE_LANG2: + case V4L2_TUNER_MODE_LANG1_LANG2: + msp_write_dsp(client, 0x000e, 0x3000); + break; + } + break; + case MSP_MODE_FM_SAT: + v4l_dbg(1, msp_debug, client, "SAT set_audmode: %s\n", modestr); + switch (audmode) { + case V4L2_TUNER_MODE_MONO: + msp3400c_set_carrier(client, MSP_CARRIER(6.5), MSP_CARRIER(6.5)); + break; + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1_LANG2: + msp3400c_set_carrier(client, MSP_CARRIER(7.2), MSP_CARRIER(7.02)); + break; + case V4L2_TUNER_MODE_LANG1: + msp3400c_set_carrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); + break; + case V4L2_TUNER_MODE_LANG2: + msp3400c_set_carrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); + break; + } + break; + case MSP_MODE_FM_NICAM1: + case MSP_MODE_FM_NICAM2: + case MSP_MODE_AM_NICAM: + v4l_dbg(1, msp_debug, client, + "NICAM set_audmode: %s\n", modestr); + if (state->nicam_on) + src = 0x0100; /* NICAM */ + break; + case MSP_MODE_BTSC: + v4l_dbg(1, msp_debug, client, + "BTSC set_audmode: %s\n", modestr); + break; + case MSP_MODE_EXTERN: + v4l_dbg(1, msp_debug, client, + "extern set_audmode: %s\n", modestr); + src = 0x0200; /* SCART */ + break; + case MSP_MODE_FM_RADIO: + v4l_dbg(1, msp_debug, client, + "FM-Radio set_audmode: %s\n", modestr); + break; + default: + v4l_dbg(1, msp_debug, client, "mono set_audmode\n"); + return; + } + + /* switch audio */ + v4l_dbg(1, msp_debug, client, "set audmode %d\n", audmode); + switch (audmode) { + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1_LANG2: + src |= 0x0020; + break; + case V4L2_TUNER_MODE_MONO: + if (state->mode == MSP_MODE_AM_NICAM) { + v4l_dbg(1, msp_debug, client, "switching to AM mono\n"); + /* AM mono decoding is handled by tuner, not MSP chip */ + /* SCART switching control register */ + msp_set_scart(client, SCART_MONO, 0); + src = 0x0200; + break; + } + if (state->rxsubchans & V4L2_TUNER_SUB_STEREO) + src = 0x0030; + break; + case V4L2_TUNER_MODE_LANG1: + break; + case V4L2_TUNER_MODE_LANG2: + src |= 0x0010; + break; + } + v4l_dbg(1, msp_debug, client, + "set_audmode final source/matrix = 0x%x\n", src); + + msp_set_source(client, src); +} + +static void msp3400c_print_mode(struct i2c_client *client) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + + if (state->main == state->second) + v4l_dbg(1, msp_debug, client, + "mono sound carrier: %d.%03d MHz\n", + state->main / 910000, (state->main / 910) % 1000); + else + v4l_dbg(1, msp_debug, client, + "main sound carrier: %d.%03d MHz\n", + state->main / 910000, (state->main / 910) % 1000); + if (state->mode == MSP_MODE_FM_NICAM1 || state->mode == MSP_MODE_FM_NICAM2) + v4l_dbg(1, msp_debug, client, + "NICAM/FM carrier : %d.%03d MHz\n", + state->second / 910000, (state->second/910) % 1000); + if (state->mode == MSP_MODE_AM_NICAM) + v4l_dbg(1, msp_debug, client, + "NICAM/AM carrier : %d.%03d MHz\n", + state->second / 910000, (state->second / 910) % 1000); + if (state->mode == MSP_MODE_FM_TERRA && state->main != state->second) { + v4l_dbg(1, msp_debug, client, + "FM-stereo carrier : %d.%03d MHz\n", + state->second / 910000, (state->second / 910) % 1000); + } +} + +/* ----------------------------------------------------------------------- */ + +static int msp3400c_detect_stereo(struct i2c_client *client) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + int val; + int rxsubchans = state->rxsubchans; + int newnicam = state->nicam_on; + int update = 0; + + switch (state->mode) { + case MSP_MODE_FM_TERRA: + val = msp_read_dsp(client, 0x18); + if (val > 32767) + val -= 65536; + v4l_dbg(2, msp_debug, client, + "stereo detect register: %d\n", val); + if (val > 8192) { + rxsubchans = V4L2_TUNER_SUB_STEREO; + } else if (val < -4096) { + rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + } else { + rxsubchans = V4L2_TUNER_SUB_MONO; + } + newnicam = 0; + break; + case MSP_MODE_FM_NICAM1: + case MSP_MODE_FM_NICAM2: + case MSP_MODE_AM_NICAM: + val = msp_read_dem(client, 0x23); + v4l_dbg(2, msp_debug, client, "nicam sync=%d, mode=%d\n", + val & 1, (val & 0x1e) >> 1); + + if (val & 1) { + /* nicam synced */ + switch ((val & 0x1e) >> 1) { + case 0: + case 8: + rxsubchans = V4L2_TUNER_SUB_STEREO; + break; + case 1: + case 9: + rxsubchans = V4L2_TUNER_SUB_MONO; + break; + case 2: + case 10: + rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + break; + default: + rxsubchans = V4L2_TUNER_SUB_MONO; + break; + } + newnicam = 1; + } else { + newnicam = 0; + rxsubchans = V4L2_TUNER_SUB_MONO; + } + break; + } + if (rxsubchans != state->rxsubchans) { + update = 1; + v4l_dbg(1, msp_debug, client, + "watch: rxsubchans %02x => %02x\n", + state->rxsubchans, rxsubchans); + state->rxsubchans = rxsubchans; + } + if (newnicam != state->nicam_on) { + update = 1; + v4l_dbg(1, msp_debug, client, "watch: nicam %d => %d\n", + state->nicam_on, newnicam); + state->nicam_on = newnicam; + } + return update; +} + +/* + * A kernel thread for msp3400 control -- we don't want to block the + * in the ioctl while doing the sound carrier & stereo detect + */ +/* stereo/multilang monitoring */ +static void watch_stereo(struct i2c_client *client) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + + if (msp_detect_stereo(client)) + msp_set_audmode(client); + + if (msp_once) + state->watch_stereo = 0; +} + +int msp3400c_thread(void *data) +{ + struct i2c_client *client = data; + struct msp_state *state = to_state(i2c_get_clientdata(client)); + struct msp3400c_carrier_detect *cd; + int count, max1, max2, val1, val2, val, i; + + v4l_dbg(1, msp_debug, client, "msp3400 daemon started\n"); + state->detected_std = V4L2_STD_ALL; + set_freezable(); + for (;;) { + v4l_dbg(2, msp_debug, client, "msp3400 thread: sleep\n"); + msp_sleep(state, -1); + v4l_dbg(2, msp_debug, client, "msp3400 thread: wakeup\n"); + +restart: + v4l_dbg(2, msp_debug, client, "thread: restart scan\n"); + state->restart = 0; + if (kthread_should_stop()) + break; + + if (state->radio || MSP_MODE_EXTERN == state->mode) { + /* no carrier scan, just unmute */ + v4l_dbg(1, msp_debug, client, + "thread: no carrier scan\n"); + state->scan_in_progress = 0; + msp_update_volume(state); + continue; + } + + /* mute audio */ + state->scan_in_progress = 1; + msp_update_volume(state); + + msp3400c_set_mode(client, MSP_MODE_AM_DETECT); + val1 = val2 = 0; + max1 = max2 = -1; + state->watch_stereo = 0; + state->nicam_on = 0; + + /* wait for tuner to settle down after a channel change */ + if (msp_sleep(state, 200)) + goto restart; + + /* carrier detect pass #1 -- main carrier */ + cd = msp3400c_carrier_detect_main; + count = ARRAY_SIZE(msp3400c_carrier_detect_main); + + if (msp_amsound && (state->v4l2_std & V4L2_STD_SECAM)) { + /* autodetect doesn't work well with AM ... */ + max1 = 3; + count = 0; + v4l_dbg(1, msp_debug, client, "AM sound override\n"); + } + + for (i = 0; i < count; i++) { + msp3400c_set_carrier(client, cd[i].cdo, cd[i].cdo); + if (msp_sleep(state, 100)) + goto restart; + val = msp_read_dsp(client, 0x1b); + if (val > 32767) + val -= 65536; + if (val1 < val) + val1 = val, max1 = i; + v4l_dbg(1, msp_debug, client, + "carrier1 val: %5d / %s\n", val, cd[i].name); + } + + /* carrier detect pass #2 -- second (stereo) carrier */ + switch (max1) { + case 1: /* 5.5 */ + cd = msp3400c_carrier_detect_55; + count = ARRAY_SIZE(msp3400c_carrier_detect_55); + break; + case 3: /* 6.5 */ + cd = msp3400c_carrier_detect_65; + count = ARRAY_SIZE(msp3400c_carrier_detect_65); + break; + case 0: /* 4.5 */ + case 2: /* 6.0 */ + default: + cd = NULL; + count = 0; + break; + } + + if (msp_amsound && (state->v4l2_std & V4L2_STD_SECAM)) { + /* autodetect doesn't work well with AM ... */ + cd = NULL; + count = 0; + max2 = 0; + } + for (i = 0; i < count; i++) { + msp3400c_set_carrier(client, cd[i].cdo, cd[i].cdo); + if (msp_sleep(state, 100)) + goto restart; + val = msp_read_dsp(client, 0x1b); + if (val > 32767) + val -= 65536; + if (val2 < val) + val2 = val, max2 = i; + v4l_dbg(1, msp_debug, client, + "carrier2 val: %5d / %s\n", val, cd[i].name); + } + + /* program the msp3400 according to the results */ + state->main = msp3400c_carrier_detect_main[max1].cdo; + switch (max1) { + case 1: /* 5.5 */ + state->detected_std = V4L2_STD_BG | V4L2_STD_PAL_H; + if (max2 == 0) { + /* B/G FM-stereo */ + state->second = msp3400c_carrier_detect_55[max2].cdo; + msp3400c_set_mode(client, MSP_MODE_FM_TERRA); + state->watch_stereo = 1; + } else if (max2 == 1 && state->has_nicam) { + /* B/G NICAM */ + state->second = msp3400c_carrier_detect_55[max2].cdo; + msp3400c_set_mode(client, MSP_MODE_FM_NICAM1); + state->nicam_on = 1; + state->watch_stereo = 1; + } else { + goto no_second; + } + break; + case 2: /* 6.0 */ + /* PAL I NICAM */ + state->detected_std = V4L2_STD_PAL_I; + state->second = MSP_CARRIER(6.552); + msp3400c_set_mode(client, MSP_MODE_FM_NICAM2); + state->nicam_on = 1; + state->watch_stereo = 1; + break; + case 3: /* 6.5 */ + if (max2 == 1 || max2 == 2) { + /* D/K FM-stereo */ + state->second = msp3400c_carrier_detect_65[max2].cdo; + msp3400c_set_mode(client, MSP_MODE_FM_TERRA); + state->watch_stereo = 1; + state->detected_std = V4L2_STD_DK; + } else if (max2 == 0 && (state->v4l2_std & V4L2_STD_SECAM)) { + /* L NICAM or AM-mono */ + state->second = msp3400c_carrier_detect_65[max2].cdo; + msp3400c_set_mode(client, MSP_MODE_AM_NICAM); + state->watch_stereo = 1; + state->detected_std = V4L2_STD_L; + } else if (max2 == 0 && state->has_nicam) { + /* D/K NICAM */ + state->second = msp3400c_carrier_detect_65[max2].cdo; + msp3400c_set_mode(client, MSP_MODE_FM_NICAM1); + state->nicam_on = 1; + state->watch_stereo = 1; + state->detected_std = V4L2_STD_DK; + } else { + goto no_second; + } + break; + case 0: /* 4.5 */ + state->detected_std = V4L2_STD_MN; + default: +no_second: + state->second = msp3400c_carrier_detect_main[max1].cdo; + msp3400c_set_mode(client, MSP_MODE_FM_TERRA); + break; + } + msp3400c_set_carrier(client, state->second, state->main); + + /* unmute */ + state->scan_in_progress = 0; + msp3400c_set_audmode(client); + msp_update_volume(state); + + if (msp_debug) + msp3400c_print_mode(client); + + /* monitor tv audio mode, the first time don't wait + so long to get a quick stereo/bilingual result */ + count = 3; + while (state->watch_stereo) { + if (msp_sleep(state, count ? 1000 : 5000)) + goto restart; + if (count) + count--; + watch_stereo(client); + } + } + v4l_dbg(1, msp_debug, client, "thread: exit\n"); + return 0; +} + + +int msp3410d_thread(void *data) +{ + struct i2c_client *client = data; + struct msp_state *state = to_state(i2c_get_clientdata(client)); + int val, i, std, count; + + v4l_dbg(1, msp_debug, client, "msp3410 daemon started\n"); + state->detected_std = V4L2_STD_ALL; + set_freezable(); + for (;;) { + v4l_dbg(2, msp_debug, client, "msp3410 thread: sleep\n"); + msp_sleep(state, -1); + v4l_dbg(2, msp_debug, client, "msp3410 thread: wakeup\n"); + +restart: + v4l_dbg(2, msp_debug, client, "thread: restart scan\n"); + state->restart = 0; + if (kthread_should_stop()) + break; + + if (state->mode == MSP_MODE_EXTERN) { + /* no carrier scan needed, just unmute */ + v4l_dbg(1, msp_debug, client, + "thread: no carrier scan\n"); + state->scan_in_progress = 0; + msp_update_volume(state); + continue; + } + + /* mute audio */ + state->scan_in_progress = 1; + msp_update_volume(state); + + /* start autodetect. Note: autodetect is not supported for + NTSC-M and radio, hence we force the standard in those + cases. */ + if (state->radio) + std = 0x40; + else + std = (state->v4l2_std & V4L2_STD_NTSC) ? 0x20 : 1; + state->watch_stereo = 0; + state->nicam_on = 0; + + /* wait for tuner to settle down after a channel change */ + if (msp_sleep(state, 200)) + goto restart; + + if (msp_debug) + v4l_dbg(2, msp_debug, client, + "setting standard: %s (0x%04x)\n", + msp_standard_std_name(std), std); + + if (std != 1) { + /* programmed some specific mode */ + val = std; + } else { + /* triggered autodetect */ + msp_write_dem(client, 0x20, std); + for (;;) { + if (msp_sleep(state, 100)) + goto restart; + + /* check results */ + val = msp_read_dem(client, 0x7e); + if (val < 0x07ff) + break; + v4l_dbg(2, msp_debug, client, + "detection still in progress\n"); + } + } + for (i = 0; msp_stdlist[i].name != NULL; i++) + if (msp_stdlist[i].retval == val) + break; + v4l_dbg(1, msp_debug, client, "current standard: %s (0x%04x)\n", + msp_standard_std_name(val), val); + state->main = msp_stdlist[i].main; + state->second = msp_stdlist[i].second; + state->std = val; + state->rxsubchans = V4L2_TUNER_SUB_MONO; + + if (msp_amsound && !state->radio && + (state->v4l2_std & V4L2_STD_SECAM) && (val != 0x0009)) { + /* autodetection has failed, let backup */ + v4l_dbg(1, msp_debug, client, "autodetection failed," + " switching to backup standard: %s (0x%04x)\n", + msp_stdlist[8].name ? + msp_stdlist[8].name : "unknown", val); + state->std = val = 0x0009; + msp_write_dem(client, 0x20, val); + } else { + state->detected_std = msp_standard_std(state->std); + } + + /* set stereo */ + switch (val) { + case 0x0008: /* B/G NICAM */ + case 0x000a: /* I NICAM */ + case 0x000b: /* D/K NICAM */ + if (val == 0x000a) + state->mode = MSP_MODE_FM_NICAM2; + else + state->mode = MSP_MODE_FM_NICAM1; + /* just turn on stereo */ + state->nicam_on = 1; + state->watch_stereo = 1; + break; + case 0x0009: + state->mode = MSP_MODE_AM_NICAM; + state->nicam_on = 1; + state->watch_stereo = 1; + break; + case 0x0020: /* BTSC */ + /* The pre-'G' models only have BTSC-mono */ + state->mode = MSP_MODE_BTSC; + break; + case 0x0040: /* FM radio */ + state->mode = MSP_MODE_FM_RADIO; + state->rxsubchans = V4L2_TUNER_SUB_STEREO; + /* not needed in theory if we have radio, but + short programming enables carrier mute */ + msp3400c_set_mode(client, MSP_MODE_FM_RADIO); + msp3400c_set_carrier(client, MSP_CARRIER(10.7), + MSP_CARRIER(10.7)); + break; + case 0x0002: + case 0x0003: + case 0x0004: + case 0x0005: + state->mode = MSP_MODE_FM_TERRA; + state->watch_stereo = 1; + break; + } + + /* set various prescales */ + msp_write_dsp(client, 0x0d, 0x1900); /* scart */ + msp_write_dsp(client, 0x0e, 0x3000); /* FM */ + if (state->has_nicam) + msp_write_dsp(client, 0x10, 0x5a00); /* nicam */ + + if (state->has_i2s_conf) + msp_write_dem(client, 0x40, state->i2s_mode); + + /* unmute */ + msp3400c_set_audmode(client); + state->scan_in_progress = 0; + msp_update_volume(state); + + /* monitor tv audio mode, the first time don't wait + so long to get a quick stereo/bilingual result */ + count = 3; + while (state->watch_stereo) { + if (msp_sleep(state, count ? 1000 : 5000)) + goto restart; + if (count) + count--; + watch_stereo(client); + } + } + v4l_dbg(1, msp_debug, client, "thread: exit\n"); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* msp34xxG + (autoselect no-thread) + * this one uses both automatic standard detection and automatic sound + * select which are available in the newer G versions + * struct msp: only norm, acb and source are really used in this mode + */ + +static int msp34xxg_modus(struct i2c_client *client) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + + if (state->radio) { + v4l_dbg(1, msp_debug, client, "selected radio modus\n"); + return 0x0001; + } + if (state->v4l2_std == V4L2_STD_NTSC_M_JP) { + v4l_dbg(1, msp_debug, client, "selected M (EIA-J) modus\n"); + return 0x4001; + } + if (state->v4l2_std == V4L2_STD_NTSC_M_KR) { + v4l_dbg(1, msp_debug, client, "selected M (A2) modus\n"); + return 0x0001; + } + if (state->v4l2_std == V4L2_STD_SECAM_L) { + v4l_dbg(1, msp_debug, client, "selected SECAM-L modus\n"); + return 0x6001; + } + if (state->v4l2_std & V4L2_STD_MN) { + v4l_dbg(1, msp_debug, client, "selected M (BTSC) modus\n"); + return 0x2001; + } + return 0x7001; +} + +static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in) + { + struct msp_state *state = to_state(i2c_get_clientdata(client)); + int source, matrix; + + switch (state->audmode) { + case V4L2_TUNER_MODE_MONO: + source = 0; /* mono only */ + matrix = 0x30; + break; + case V4L2_TUNER_MODE_LANG2: + source = 4; /* stereo or B */ + matrix = 0x10; + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + source = 1; /* stereo or A|B */ + matrix = 0x20; + break; + case V4L2_TUNER_MODE_LANG1: + source = 3; /* stereo or A */ + matrix = 0x00; + break; + case V4L2_TUNER_MODE_STEREO: + default: + source = 3; /* stereo or A */ + matrix = 0x20; + break; + } + + if (in == MSP_DSP_IN_TUNER) + source = (source << 8) | 0x20; + /* the msp34x2g puts the MAIN_AVC, MAIN and AUX sources in 12, 13, 14 + instead of 11, 12, 13. So we add one for that msp version. */ + else if (in >= MSP_DSP_IN_MAIN_AVC && state->has_dolby_pro_logic) + source = ((in + 1) << 8) | matrix; + else + source = (in << 8) | matrix; + + v4l_dbg(1, msp_debug, client, + "set source to %d (0x%x) for output %02x\n", in, source, reg); + msp_write_dsp(client, reg, source); +} + +static void msp34xxg_set_sources(struct i2c_client *client) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + u32 in = state->route_in; + + msp34xxg_set_source(client, 0x0008, (in >> 4) & 0xf); + /* quasi-peak detector is set to same input as the loudspeaker (MAIN) */ + msp34xxg_set_source(client, 0x000c, (in >> 4) & 0xf); + msp34xxg_set_source(client, 0x0009, (in >> 8) & 0xf); + msp34xxg_set_source(client, 0x000a, (in >> 12) & 0xf); + if (state->has_scart2_out) + msp34xxg_set_source(client, 0x0041, (in >> 16) & 0xf); + msp34xxg_set_source(client, 0x000b, (in >> 20) & 0xf); +} + +/* (re-)initialize the msp34xxg */ +static void msp34xxg_reset(struct i2c_client *client) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + int tuner = (state->route_in >> 3) & 1; + int modus; + + /* initialize std to 1 (autodetect) to signal that no standard is + selected yet. */ + state->std = 1; + + msp_reset(client); + + if (state->has_i2s_conf) + msp_write_dem(client, 0x40, state->i2s_mode); + + /* step-by-step initialisation, as described in the manual */ + modus = msp34xxg_modus(client); + modus |= tuner ? 0x100 : 0; + msp_write_dem(client, 0x30, modus); + + /* write the dsps that may have an influence on + standard/audio autodetection right now */ + msp34xxg_set_sources(client); + + msp_write_dsp(client, 0x0d, 0x1900); /* scart */ + msp_write_dsp(client, 0x0e, 0x3000); /* FM */ + if (state->has_nicam) + msp_write_dsp(client, 0x10, 0x5a00); /* nicam */ + + /* set identification threshold. Personally, I + * I set it to a higher value than the default + * of 0x190 to ignore noisy stereo signals. + * this needs tuning. (recommended range 0x00a0-0x03c0) + * 0x7f0 = forced mono mode + * + * a2 threshold for stereo/bilingual. + * Note: this register is part of the Manual/Compatibility mode. + * It is supported by all 'G'-family chips. + */ + msp_write_dem(client, 0x22, msp_stereo_thresh); +} + +int msp34xxg_thread(void *data) +{ + struct i2c_client *client = data; + struct msp_state *state = to_state(i2c_get_clientdata(client)); + int val, i; + + v4l_dbg(1, msp_debug, client, "msp34xxg daemon started\n"); + state->detected_std = V4L2_STD_ALL; + set_freezable(); + for (;;) { + v4l_dbg(2, msp_debug, client, "msp34xxg thread: sleep\n"); + msp_sleep(state, -1); + v4l_dbg(2, msp_debug, client, "msp34xxg thread: wakeup\n"); + +restart: + v4l_dbg(1, msp_debug, client, "thread: restart scan\n"); + state->restart = 0; + if (kthread_should_stop()) + break; + + if (state->mode == MSP_MODE_EXTERN) { + /* no carrier scan needed, just unmute */ + v4l_dbg(1, msp_debug, client, + "thread: no carrier scan\n"); + state->scan_in_progress = 0; + msp_update_volume(state); + continue; + } + + /* setup the chip*/ + msp34xxg_reset(client); + state->std = state->radio ? 0x40 : + (state->force_btsc && msp_standard == 1) ? 32 : msp_standard; + msp_write_dem(client, 0x20, state->std); + /* start autodetect */ + if (state->std != 1) + goto unmute; + + /* watch autodetect */ + v4l_dbg(1, msp_debug, client, + "started autodetect, waiting for result\n"); + for (i = 0; i < 10; i++) { + if (msp_sleep(state, 100)) + goto restart; + + /* check results */ + val = msp_read_dem(client, 0x7e); + if (val < 0x07ff) { + state->std = val; + break; + } + v4l_dbg(2, msp_debug, client, + "detection still in progress\n"); + } + if (state->std == 1) { + v4l_dbg(1, msp_debug, client, + "detection still in progress after 10 tries. giving up.\n"); + continue; + } + +unmute: + v4l_dbg(1, msp_debug, client, + "detected standard: %s (0x%04x)\n", + msp_standard_std_name(state->std), state->std); + state->detected_std = msp_standard_std(state->std); + + if (state->std == 9) { + /* AM NICAM mode */ + msp_write_dsp(client, 0x0e, 0x7c00); + } + + /* unmute: dispatch sound to scart output, set scart volume */ + msp_update_volume(state); + + /* restore ACB */ + if (msp_write_dsp(client, 0x13, state->acb)) + return -1; + + /* the periodic stereo/SAP check is only relevant for + the 0x20 standard (BTSC) */ + if (state->std != 0x20) + continue; + + state->watch_stereo = 1; + + /* monitor tv audio mode, the first time don't wait + in order to get a quick stereo/SAP update */ + watch_stereo(client); + while (state->watch_stereo) { + watch_stereo(client); + if (msp_sleep(state, 5000)) + goto restart; + } + } + v4l_dbg(1, msp_debug, client, "thread: exit\n"); + return 0; +} + +static int msp34xxg_detect_stereo(struct i2c_client *client) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + int status = msp_read_dem(client, 0x0200); + int is_bilingual = status & 0x100; + int is_stereo = status & 0x40; + int oldrx = state->rxsubchans; + + if (state->mode == MSP_MODE_EXTERN) + return 0; + + state->rxsubchans = 0; + if (is_stereo) + state->rxsubchans = V4L2_TUNER_SUB_STEREO; + else + state->rxsubchans = V4L2_TUNER_SUB_MONO; + if (is_bilingual) { + if (state->std == 0x20) + state->rxsubchans |= V4L2_TUNER_SUB_SAP; + else + state->rxsubchans = + V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + } + v4l_dbg(1, msp_debug, client, + "status=0x%x, stereo=%d, bilingual=%d -> rxsubchans=%d\n", + status, is_stereo, is_bilingual, state->rxsubchans); + return (oldrx != state->rxsubchans); +} + +static void msp34xxg_set_audmode(struct i2c_client *client) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + + if (state->std == 0x20) { + if ((state->rxsubchans & V4L2_TUNER_SUB_SAP) && + (state->audmode == V4L2_TUNER_MODE_LANG1_LANG2 || + state->audmode == V4L2_TUNER_MODE_LANG2)) { + msp_write_dem(client, 0x20, 0x21); + } else { + msp_write_dem(client, 0x20, 0x20); + } + } + + msp34xxg_set_sources(client); +} + +void msp_set_audmode(struct i2c_client *client) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + + switch (state->opmode) { + case OPMODE_MANUAL: + case OPMODE_AUTODETECT: + msp3400c_set_audmode(client); + break; + case OPMODE_AUTOSELECT: + msp34xxg_set_audmode(client); + break; + } +} + +int msp_detect_stereo(struct i2c_client *client) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + + switch (state->opmode) { + case OPMODE_MANUAL: + case OPMODE_AUTODETECT: + return msp3400c_detect_stereo(client); + case OPMODE_AUTOSELECT: + return msp34xxg_detect_stereo(client); + } + return 0; +} + diff --git a/drivers/media/i2c/mt9m032.c b/drivers/media/i2c/mt9m032.c new file mode 100644 index 00000000000..445359c9611 --- /dev/null +++ b/drivers/media/i2c/mt9m032.c @@ -0,0 +1,878 @@ +/* + * Driver for MT9M032 CMOS Image Sensor from Micron + * + * Copyright (C) 2010-2011 Lund Engineering + * Contact: Gil Lund + * Author: Martin Hostettler + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "aptina-pll.h" + +/* + * width and height include active boundary and black parts + * + * column 0- 15 active boundary + * column 16-1455 image + * column 1456-1471 active boundary + * column 1472-1599 black + * + * row 0- 51 black + * row 53- 59 active boundary + * row 60-1139 image + * row 1140-1147 active boundary + * row 1148-1151 black + */ + +#define MT9M032_PIXEL_ARRAY_WIDTH 1600 +#define MT9M032_PIXEL_ARRAY_HEIGHT 1152 + +#define MT9M032_CHIP_VERSION 0x00 +#define MT9M032_CHIP_VERSION_VALUE 0x1402 +#define MT9M032_ROW_START 0x01 +#define MT9M032_ROW_START_MIN 0 +#define MT9M032_ROW_START_MAX 1152 +#define MT9M032_ROW_START_DEF 60 +#define MT9M032_COLUMN_START 0x02 +#define MT9M032_COLUMN_START_MIN 0 +#define MT9M032_COLUMN_START_MAX 1600 +#define MT9M032_COLUMN_START_DEF 16 +#define MT9M032_ROW_SIZE 0x03 +#define MT9M032_ROW_SIZE_MIN 32 +#define MT9M032_ROW_SIZE_MAX 1152 +#define MT9M032_ROW_SIZE_DEF 1080 +#define MT9M032_COLUMN_SIZE 0x04 +#define MT9M032_COLUMN_SIZE_MIN 32 +#define MT9M032_COLUMN_SIZE_MAX 1600 +#define MT9M032_COLUMN_SIZE_DEF 1440 +#define MT9M032_HBLANK 0x05 +#define MT9M032_VBLANK 0x06 +#define MT9M032_VBLANK_MAX 0x7ff +#define MT9M032_SHUTTER_WIDTH_HIGH 0x08 +#define MT9M032_SHUTTER_WIDTH_LOW 0x09 +#define MT9M032_SHUTTER_WIDTH_MIN 1 +#define MT9M032_SHUTTER_WIDTH_MAX 1048575 +#define MT9M032_SHUTTER_WIDTH_DEF 1943 +#define MT9M032_PIX_CLK_CTRL 0x0a +#define MT9M032_PIX_CLK_CTRL_INV_PIXCLK 0x8000 +#define MT9M032_RESTART 0x0b +#define MT9M032_RESET 0x0d +#define MT9M032_PLL_CONFIG1 0x11 +#define MT9M032_PLL_CONFIG1_OUTDIV_MASK 0x3f +#define MT9M032_PLL_CONFIG1_MUL_SHIFT 8 +#define MT9M032_READ_MODE1 0x1e +#define MT9M032_READ_MODE2 0x20 +#define MT9M032_READ_MODE2_VFLIP_SHIFT 15 +#define MT9M032_READ_MODE2_HFLIP_SHIFT 14 +#define MT9M032_READ_MODE2_ROW_BLC 0x40 +#define MT9M032_GAIN_GREEN1 0x2b +#define MT9M032_GAIN_BLUE 0x2c +#define MT9M032_GAIN_RED 0x2d +#define MT9M032_GAIN_GREEN2 0x2e + +/* write only */ +#define MT9M032_GAIN_ALL 0x35 +#define MT9M032_GAIN_DIGITAL_MASK 0x7f +#define MT9M032_GAIN_DIGITAL_SHIFT 8 +#define MT9M032_GAIN_AMUL_SHIFT 6 +#define MT9M032_GAIN_ANALOG_MASK 0x3f +#define MT9M032_FORMATTER1 0x9e +#define MT9M032_FORMATTER2 0x9f +#define MT9M032_FORMATTER2_DOUT_EN 0x1000 +#define MT9M032_FORMATTER2_PIXCLK_EN 0x2000 + +/* + * The available MT9M032 datasheet is missing documentation for register 0x10 + * MT9P031 seems to be close enough, so use constants from that datasheet for + * now. + * But keep the name MT9P031 to remind us, that this isn't really confirmed + * for this sensor. + */ +#define MT9P031_PLL_CONTROL 0x10 +#define MT9P031_PLL_CONTROL_PWROFF 0x0050 +#define MT9P031_PLL_CONTROL_PWRON 0x0051 +#define MT9P031_PLL_CONTROL_USEPLL 0x0052 +#define MT9P031_PLL_CONFIG2 0x11 +#define MT9P031_PLL_CONFIG2_P1_DIV_MASK 0x1f + +struct mt9m032 { + struct v4l2_subdev subdev; + struct media_pad pad; + struct mt9m032_platform_data *pdata; + + unsigned int pix_clock; + + struct v4l2_ctrl_handler ctrls; + struct { + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + }; + + struct mutex lock; /* Protects streaming, format, interval and crop */ + + bool streaming; + + struct v4l2_mbus_framefmt format; + struct v4l2_rect crop; + struct v4l2_fract frame_interval; +}; + +#define to_mt9m032(sd) container_of(sd, struct mt9m032, subdev) +#define to_dev(sensor) \ + (&((struct i2c_client *)v4l2_get_subdevdata(&(sensor)->subdev))->dev) + +static int mt9m032_read(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_word_swapped(client, reg); +} + +static int mt9m032_write(struct i2c_client *client, u8 reg, const u16 data) +{ + return i2c_smbus_write_word_swapped(client, reg, data); +} + +static u32 mt9m032_row_time(struct mt9m032 *sensor, unsigned int width) +{ + unsigned int effective_width; + u32 ns; + + effective_width = width + 716; /* empirical value */ + ns = div_u64(1000000000ULL * effective_width, sensor->pix_clock); + dev_dbg(to_dev(sensor), "MT9M032 line time: %u ns\n", ns); + return ns; +} + +static int mt9m032_update_timing(struct mt9m032 *sensor, + struct v4l2_fract *interval) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + struct v4l2_rect *crop = &sensor->crop; + unsigned int min_vblank; + unsigned int vblank; + u32 row_time; + + if (!interval) + interval = &sensor->frame_interval; + + row_time = mt9m032_row_time(sensor, crop->width); + + vblank = div_u64(1000000000ULL * interval->numerator, + (u64)row_time * interval->denominator) + - crop->height; + + if (vblank > MT9M032_VBLANK_MAX) { + /* hardware limits to 11 bit values */ + interval->denominator = 1000; + interval->numerator = + div_u64((crop->height + MT9M032_VBLANK_MAX) * + (u64)row_time * interval->denominator, + 1000000000ULL); + vblank = div_u64(1000000000ULL * interval->numerator, + (u64)row_time * interval->denominator) + - crop->height; + } + /* enforce minimal 1.6ms blanking time. */ + min_vblank = 1600000 / row_time; + vblank = clamp_t(unsigned int, vblank, min_vblank, MT9M032_VBLANK_MAX); + + return mt9m032_write(client, MT9M032_VBLANK, vblank); +} + +static int mt9m032_update_geom_timing(struct mt9m032 *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + int ret; + + ret = mt9m032_write(client, MT9M032_COLUMN_SIZE, + sensor->crop.width - 1); + if (!ret) + ret = mt9m032_write(client, MT9M032_ROW_SIZE, + sensor->crop.height - 1); + if (!ret) + ret = mt9m032_write(client, MT9M032_COLUMN_START, + sensor->crop.left); + if (!ret) + ret = mt9m032_write(client, MT9M032_ROW_START, + sensor->crop.top); + if (!ret) + ret = mt9m032_update_timing(sensor, NULL); + return ret; +} + +static int update_formatter2(struct mt9m032 *sensor, bool streaming) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + u16 reg_val = MT9M032_FORMATTER2_DOUT_EN + | 0x0070; /* parts reserved! */ + /* possibly for changing to 14-bit mode */ + + if (streaming) + reg_val |= MT9M032_FORMATTER2_PIXCLK_EN; /* pixclock enable */ + + return mt9m032_write(client, MT9M032_FORMATTER2, reg_val); +} + +static int mt9m032_setup_pll(struct mt9m032 *sensor) +{ + static const struct aptina_pll_limits limits = { + .ext_clock_min = 8000000, + .ext_clock_max = 16500000, + .int_clock_min = 2000000, + .int_clock_max = 24000000, + .out_clock_min = 322000000, + .out_clock_max = 693000000, + .pix_clock_max = 99000000, + .n_min = 1, + .n_max = 64, + .m_min = 16, + .m_max = 255, + .p1_min = 1, + .p1_max = 128, + }; + + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + struct mt9m032_platform_data *pdata = sensor->pdata; + struct aptina_pll pll; + int ret; + + pll.ext_clock = pdata->ext_clock; + pll.pix_clock = pdata->pix_clock; + + ret = aptina_pll_calculate(&client->dev, &limits, &pll); + if (ret < 0) + return ret; + + sensor->pix_clock = pdata->pix_clock; + + ret = mt9m032_write(client, MT9M032_PLL_CONFIG1, + (pll.m << MT9M032_PLL_CONFIG1_MUL_SHIFT) + | (pll.p1 - 1)); + if (!ret) + ret = mt9m032_write(client, MT9P031_PLL_CONFIG2, pll.n - 1); + if (!ret) + ret = mt9m032_write(client, MT9P031_PLL_CONTROL, + MT9P031_PLL_CONTROL_PWRON | + MT9P031_PLL_CONTROL_USEPLL); + if (!ret) /* more reserved, Continuous, Master Mode */ + ret = mt9m032_write(client, MT9M032_READ_MODE1, 0x8006); + if (!ret) /* Set 14-bit mode, select 7 divider */ + ret = mt9m032_write(client, MT9M032_FORMATTER1, 0x111e); + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Subdev pad operations + */ + +static int mt9m032_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + + code->code = V4L2_MBUS_FMT_Y8_1X8; + return 0; +} + +static int mt9m032_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index != 0 || fse->code != V4L2_MBUS_FMT_Y8_1X8) + return -EINVAL; + + fse->min_width = MT9M032_COLUMN_SIZE_DEF; + fse->max_width = MT9M032_COLUMN_SIZE_DEF; + fse->min_height = MT9M032_ROW_SIZE_DEF; + fse->max_height = MT9M032_ROW_SIZE_DEF; + + return 0; +} + +/** + * __mt9m032_get_pad_crop() - get crop rect + * @sensor: pointer to the sensor struct + * @fh: file handle for getting the try crop rect from + * @which: select try or active crop rect + * + * Returns a pointer the current active or fh relative try crop rect + */ +static struct v4l2_rect * +__mt9m032_get_pad_crop(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh, + enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(fh, 0); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &sensor->crop; + default: + return NULL; + } +} + +/** + * __mt9m032_get_pad_format() - get format + * @sensor: pointer to the sensor struct + * @fh: file handle for getting the try format from + * @which: select try or active format + * + * Returns a pointer the current active or fh relative try format + */ +static struct v4l2_mbus_framefmt * +__mt9m032_get_pad_format(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh, + enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(fh, 0); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &sensor->format; + default: + return NULL; + } +} + +static int mt9m032_get_pad_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + + mutex_lock(&sensor->lock); + fmt->format = *__mt9m032_get_pad_format(sensor, fh, fmt->which); + mutex_unlock(&sensor->lock); + + return 0; +} + +static int mt9m032_set_pad_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + int ret; + + mutex_lock(&sensor->lock); + + if (sensor->streaming && fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + ret = -EBUSY; + goto done; + } + + /* Scaling is not supported, the format is thus fixed. */ + fmt->format = *__mt9m032_get_pad_format(sensor, fh, fmt->which); + ret = 0; + +done: + mutex_unlock(&sensor->lock); + return ret; +} + +static int mt9m032_get_pad_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + + mutex_lock(&sensor->lock); + crop->rect = *__mt9m032_get_pad_crop(sensor, fh, crop->which); + mutex_unlock(&sensor->lock); + + return 0; +} + +static int mt9m032_set_pad_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *__crop; + struct v4l2_rect rect; + int ret = 0; + + mutex_lock(&sensor->lock); + + if (sensor->streaming && crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + ret = -EBUSY; + goto done; + } + + /* Clamp the crop rectangle boundaries and align them to a multiple of 2 + * pixels to ensure a GRBG Bayer pattern. + */ + rect.left = clamp(ALIGN(crop->rect.left, 2), MT9M032_COLUMN_START_MIN, + MT9M032_COLUMN_START_MAX); + rect.top = clamp(ALIGN(crop->rect.top, 2), MT9M032_ROW_START_MIN, + MT9M032_ROW_START_MAX); + rect.width = clamp(ALIGN(crop->rect.width, 2), MT9M032_COLUMN_SIZE_MIN, + MT9M032_COLUMN_SIZE_MAX); + rect.height = clamp(ALIGN(crop->rect.height, 2), MT9M032_ROW_SIZE_MIN, + MT9M032_ROW_SIZE_MAX); + + rect.width = min(rect.width, MT9M032_PIXEL_ARRAY_WIDTH - rect.left); + rect.height = min(rect.height, MT9M032_PIXEL_ARRAY_HEIGHT - rect.top); + + __crop = __mt9m032_get_pad_crop(sensor, fh, crop->which); + + if (rect.width != __crop->width || rect.height != __crop->height) { + /* Reset the output image size if the crop rectangle size has + * been modified. + */ + format = __mt9m032_get_pad_format(sensor, fh, crop->which); + format->width = rect.width; + format->height = rect.height; + } + + *__crop = rect; + crop->rect = rect; + + if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) + ret = mt9m032_update_geom_timing(sensor); + +done: + mutex_unlock(&sensor->lock); + return ret; +} + +static int mt9m032_get_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_frame_interval *fi) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + + mutex_lock(&sensor->lock); + memset(fi, 0, sizeof(*fi)); + fi->interval = sensor->frame_interval; + mutex_unlock(&sensor->lock); + + return 0; +} + +static int mt9m032_set_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_frame_interval *fi) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + int ret; + + mutex_lock(&sensor->lock); + + if (sensor->streaming) { + ret = -EBUSY; + goto done; + } + + /* Avoid divisions by 0. */ + if (fi->interval.denominator == 0) + fi->interval.denominator = 1; + + ret = mt9m032_update_timing(sensor, &fi->interval); + if (!ret) + sensor->frame_interval = fi->interval; + +done: + mutex_unlock(&sensor->lock); + return ret; +} + +static int mt9m032_s_stream(struct v4l2_subdev *subdev, int streaming) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + int ret; + + mutex_lock(&sensor->lock); + ret = update_formatter2(sensor, streaming); + if (!ret) + sensor->streaming = streaming; + mutex_unlock(&sensor->lock); + + return ret; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev core operations + */ + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int mt9m032_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct mt9m032 *sensor = to_mt9m032(sd); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + int val; + + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) + return -EINVAL; + if (reg->match.addr != client->addr) + return -ENODEV; + + val = mt9m032_read(client, reg->reg); + if (val < 0) + return -EIO; + + reg->size = 2; + reg->val = val; + + return 0; +} + +static int mt9m032_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct mt9m032 *sensor = to_mt9m032(sd); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) + return -EINVAL; + + if (reg->match.addr != client->addr) + return -ENODEV; + + return mt9m032_write(client, reg->reg, reg->val); +} +#endif + +/* ----------------------------------------------------------------------------- + * V4L2 subdev control operations + */ + +static int update_read_mode2(struct mt9m032 *sensor, bool vflip, bool hflip) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + int reg_val = (vflip << MT9M032_READ_MODE2_VFLIP_SHIFT) + | (hflip << MT9M032_READ_MODE2_HFLIP_SHIFT) + | MT9M032_READ_MODE2_ROW_BLC + | 0x0007; + + return mt9m032_write(client, MT9M032_READ_MODE2, reg_val); +} + +static int mt9m032_set_gain(struct mt9m032 *sensor, s32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + int digital_gain_val; /* in 1/8th (0..127) */ + int analog_mul; /* 0 or 1 */ + int analog_gain_val; /* in 1/16th. (0..63) */ + u16 reg_val; + + digital_gain_val = 51; /* from setup example */ + + if (val < 63) { + analog_mul = 0; + analog_gain_val = val; + } else { + analog_mul = 1; + analog_gain_val = val / 2; + } + + /* a_gain = (1 + analog_mul) + (analog_gain_val + 1) / 16 */ + /* overall_gain = a_gain * (1 + digital_gain_val / 8) */ + + reg_val = ((digital_gain_val & MT9M032_GAIN_DIGITAL_MASK) + << MT9M032_GAIN_DIGITAL_SHIFT) + | ((analog_mul & 1) << MT9M032_GAIN_AMUL_SHIFT) + | (analog_gain_val & MT9M032_GAIN_ANALOG_MASK); + + return mt9m032_write(client, MT9M032_GAIN_ALL, reg_val); +} + +static int mt9m032_try_ctrl(struct v4l2_ctrl *ctrl) +{ + if (ctrl->id == V4L2_CID_GAIN && ctrl->val >= 63) { + /* round because of multiplier used for values >= 63 */ + ctrl->val &= ~1; + } + + return 0; +} + +static int mt9m032_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9m032 *sensor = + container_of(ctrl->handler, struct mt9m032, ctrls); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + int ret; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + return mt9m032_set_gain(sensor, ctrl->val); + + case V4L2_CID_HFLIP: + /* case V4L2_CID_VFLIP: -- In the same cluster */ + return update_read_mode2(sensor, sensor->vflip->val, + sensor->hflip->val); + + case V4L2_CID_EXPOSURE: + ret = mt9m032_write(client, MT9M032_SHUTTER_WIDTH_HIGH, + (ctrl->val >> 16) & 0xffff); + if (ret < 0) + return ret; + + return mt9m032_write(client, MT9M032_SHUTTER_WIDTH_LOW, + ctrl->val & 0xffff); + } + + return 0; +} + +static struct v4l2_ctrl_ops mt9m032_ctrl_ops = { + .s_ctrl = mt9m032_set_ctrl, + .try_ctrl = mt9m032_try_ctrl, +}; + +/* -------------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops mt9m032_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = mt9m032_g_register, + .s_register = mt9m032_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops mt9m032_video_ops = { + .s_stream = mt9m032_s_stream, + .g_frame_interval = mt9m032_get_frame_interval, + .s_frame_interval = mt9m032_set_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops mt9m032_pad_ops = { + .enum_mbus_code = mt9m032_enum_mbus_code, + .enum_frame_size = mt9m032_enum_frame_size, + .get_fmt = mt9m032_get_pad_format, + .set_fmt = mt9m032_set_pad_format, + .set_crop = mt9m032_set_pad_crop, + .get_crop = mt9m032_get_pad_crop, +}; + +static const struct v4l2_subdev_ops mt9m032_ops = { + .core = &mt9m032_core_ops, + .video = &mt9m032_video_ops, + .pad = &mt9m032_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * Driver initialization and probing + */ + +static int mt9m032_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + struct mt9m032_platform_data *pdata = client->dev.platform_data; + struct i2c_adapter *adapter = client->adapter; + struct mt9m032 *sensor; + int chip_version; + int ret; + + if (pdata == NULL) { + dev_err(&client->dev, "No platform data\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { + dev_warn(&client->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + if (!client->dev.platform_data) + return -ENODEV; + + sensor = kzalloc(sizeof(*sensor), GFP_KERNEL); + if (sensor == NULL) + return -ENOMEM; + + mutex_init(&sensor->lock); + + sensor->pdata = pdata; + + v4l2_i2c_subdev_init(&sensor->subdev, client, &mt9m032_ops); + sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + chip_version = mt9m032_read(client, MT9M032_CHIP_VERSION); + if (chip_version != MT9M032_CHIP_VERSION_VALUE) { + dev_err(&client->dev, "MT9M032 not detected, wrong version " + "0x%04x\n", chip_version); + ret = -ENODEV; + goto error_sensor; + } + + dev_info(&client->dev, "MT9M032 detected at address 0x%02x\n", + client->addr); + + sensor->frame_interval.numerator = 1; + sensor->frame_interval.denominator = 30; + + sensor->crop.left = MT9M032_COLUMN_START_DEF; + sensor->crop.top = MT9M032_ROW_START_DEF; + sensor->crop.width = MT9M032_COLUMN_SIZE_DEF; + sensor->crop.height = MT9M032_ROW_SIZE_DEF; + + sensor->format.width = sensor->crop.width; + sensor->format.height = sensor->crop.height; + sensor->format.code = V4L2_MBUS_FMT_Y8_1X8; + sensor->format.field = V4L2_FIELD_NONE; + sensor->format.colorspace = V4L2_COLORSPACE_SRGB; + + v4l2_ctrl_handler_init(&sensor->ctrls, 5); + + v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops, + V4L2_CID_GAIN, 0, 127, 1, 64); + + sensor->hflip = v4l2_ctrl_new_std(&sensor->ctrls, + &mt9m032_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + sensor->vflip = v4l2_ctrl_new_std(&sensor->ctrls, + &mt9m032_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops, + V4L2_CID_EXPOSURE, MT9M032_SHUTTER_WIDTH_MIN, + MT9M032_SHUTTER_WIDTH_MAX, 1, + MT9M032_SHUTTER_WIDTH_DEF); + v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops, + V4L2_CID_PIXEL_RATE, pdata->pix_clock, + pdata->pix_clock, 1, pdata->pix_clock); + + if (sensor->ctrls.error) { + ret = sensor->ctrls.error; + dev_err(&client->dev, "control initialization error %d\n", ret); + goto error_ctrl; + } + + v4l2_ctrl_cluster(2, &sensor->hflip); + + sensor->subdev.ctrl_handler = &sensor->ctrls; + sensor->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&sensor->subdev.entity, 1, &sensor->pad, 0); + if (ret < 0) + goto error_ctrl; + + ret = mt9m032_write(client, MT9M032_RESET, 1); /* reset on */ + if (ret < 0) + goto error_entity; + mt9m032_write(client, MT9M032_RESET, 0); /* reset off */ + if (ret < 0) + goto error_entity; + + ret = mt9m032_setup_pll(sensor); + if (ret < 0) + goto error_entity; + usleep_range(10000, 11000); + + ret = v4l2_ctrl_handler_setup(&sensor->ctrls); + if (ret < 0) + goto error_entity; + + /* SIZE */ + ret = mt9m032_update_geom_timing(sensor); + if (ret < 0) + goto error_entity; + + ret = mt9m032_write(client, 0x41, 0x0000); /* reserved !!! */ + if (ret < 0) + goto error_entity; + ret = mt9m032_write(client, 0x42, 0x0003); /* reserved !!! */ + if (ret < 0) + goto error_entity; + ret = mt9m032_write(client, 0x43, 0x0003); /* reserved !!! */ + if (ret < 0) + goto error_entity; + ret = mt9m032_write(client, 0x7f, 0x0000); /* reserved !!! */ + if (ret < 0) + goto error_entity; + if (sensor->pdata->invert_pixclock) { + ret = mt9m032_write(client, MT9M032_PIX_CLK_CTRL, + MT9M032_PIX_CLK_CTRL_INV_PIXCLK); + if (ret < 0) + goto error_entity; + } + + ret = mt9m032_write(client, MT9M032_RESTART, 1); /* Restart on */ + if (ret < 0) + goto error_entity; + msleep(100); + ret = mt9m032_write(client, MT9M032_RESTART, 0); /* Restart off */ + if (ret < 0) + goto error_entity; + msleep(100); + ret = update_formatter2(sensor, false); + if (ret < 0) + goto error_entity; + + return ret; + +error_entity: + media_entity_cleanup(&sensor->subdev.entity); +error_ctrl: + v4l2_ctrl_handler_free(&sensor->ctrls); +error_sensor: + mutex_destroy(&sensor->lock); + kfree(sensor); + return ret; +} + +static int mt9m032_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct mt9m032 *sensor = to_mt9m032(subdev); + + v4l2_device_unregister_subdev(subdev); + v4l2_ctrl_handler_free(&sensor->ctrls); + media_entity_cleanup(&subdev->entity); + mutex_destroy(&sensor->lock); + kfree(sensor); + return 0; +} + +static const struct i2c_device_id mt9m032_id_table[] = { + { MT9M032_NAME, 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, mt9m032_id_table); + +static struct i2c_driver mt9m032_i2c_driver = { + .driver = { + .name = MT9M032_NAME, + }, + .probe = mt9m032_probe, + .remove = mt9m032_remove, + .id_table = mt9m032_id_table, +}; + +module_i2c_driver(mt9m032_i2c_driver); + +MODULE_AUTHOR("Martin Hostettler "); +MODULE_DESCRIPTION("MT9M032 camera sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c new file mode 100644 index 00000000000..3be537ef22d --- /dev/null +++ b/drivers/media/i2c/mt9p031.c @@ -0,0 +1,1071 @@ +/* + * Driver for MT9P031 CMOS Image Sensor from Aptina + * + * Copyright (C) 2011, Laurent Pinchart + * Copyright (C) 2011, Javier Martin + * Copyright (C) 2011, Guennadi Liakhovetski + * + * Based on the MT9V032 driver and Bastian Hecht's code. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "aptina-pll.h" + +#define MT9P031_PIXEL_ARRAY_WIDTH 2752 +#define MT9P031_PIXEL_ARRAY_HEIGHT 2004 + +#define MT9P031_CHIP_VERSION 0x00 +#define MT9P031_CHIP_VERSION_VALUE 0x1801 +#define MT9P031_ROW_START 0x01 +#define MT9P031_ROW_START_MIN 0 +#define MT9P031_ROW_START_MAX 2004 +#define MT9P031_ROW_START_DEF 54 +#define MT9P031_COLUMN_START 0x02 +#define MT9P031_COLUMN_START_MIN 0 +#define MT9P031_COLUMN_START_MAX 2750 +#define MT9P031_COLUMN_START_DEF 16 +#define MT9P031_WINDOW_HEIGHT 0x03 +#define MT9P031_WINDOW_HEIGHT_MIN 2 +#define MT9P031_WINDOW_HEIGHT_MAX 2006 +#define MT9P031_WINDOW_HEIGHT_DEF 1944 +#define MT9P031_WINDOW_WIDTH 0x04 +#define MT9P031_WINDOW_WIDTH_MIN 2 +#define MT9P031_WINDOW_WIDTH_MAX 2752 +#define MT9P031_WINDOW_WIDTH_DEF 2592 +#define MT9P031_HORIZONTAL_BLANK 0x05 +#define MT9P031_HORIZONTAL_BLANK_MIN 0 +#define MT9P031_HORIZONTAL_BLANK_MAX 4095 +#define MT9P031_VERTICAL_BLANK 0x06 +#define MT9P031_VERTICAL_BLANK_MIN 0 +#define MT9P031_VERTICAL_BLANK_MAX 4095 +#define MT9P031_VERTICAL_BLANK_DEF 25 +#define MT9P031_OUTPUT_CONTROL 0x07 +#define MT9P031_OUTPUT_CONTROL_CEN 2 +#define MT9P031_OUTPUT_CONTROL_SYN 1 +#define MT9P031_OUTPUT_CONTROL_DEF 0x1f82 +#define MT9P031_SHUTTER_WIDTH_UPPER 0x08 +#define MT9P031_SHUTTER_WIDTH_LOWER 0x09 +#define MT9P031_SHUTTER_WIDTH_MIN 1 +#define MT9P031_SHUTTER_WIDTH_MAX 1048575 +#define MT9P031_SHUTTER_WIDTH_DEF 1943 +#define MT9P031_PLL_CONTROL 0x10 +#define MT9P031_PLL_CONTROL_PWROFF 0x0050 +#define MT9P031_PLL_CONTROL_PWRON 0x0051 +#define MT9P031_PLL_CONTROL_USEPLL 0x0052 +#define MT9P031_PLL_CONFIG_1 0x11 +#define MT9P031_PLL_CONFIG_2 0x12 +#define MT9P031_PIXEL_CLOCK_CONTROL 0x0a +#define MT9P031_FRAME_RESTART 0x0b +#define MT9P031_SHUTTER_DELAY 0x0c +#define MT9P031_RST 0x0d +#define MT9P031_RST_ENABLE 1 +#define MT9P031_RST_DISABLE 0 +#define MT9P031_READ_MODE_1 0x1e +#define MT9P031_READ_MODE_2 0x20 +#define MT9P031_READ_MODE_2_ROW_MIR (1 << 15) +#define MT9P031_READ_MODE_2_COL_MIR (1 << 14) +#define MT9P031_READ_MODE_2_ROW_BLC (1 << 6) +#define MT9P031_ROW_ADDRESS_MODE 0x22 +#define MT9P031_COLUMN_ADDRESS_MODE 0x23 +#define MT9P031_GLOBAL_GAIN 0x35 +#define MT9P031_GLOBAL_GAIN_MIN 8 +#define MT9P031_GLOBAL_GAIN_MAX 1024 +#define MT9P031_GLOBAL_GAIN_DEF 8 +#define MT9P031_GLOBAL_GAIN_MULT (1 << 6) +#define MT9P031_ROW_BLACK_TARGET 0x49 +#define MT9P031_ROW_BLACK_DEF_OFFSET 0x4b +#define MT9P031_GREEN1_OFFSET 0x60 +#define MT9P031_GREEN2_OFFSET 0x61 +#define MT9P031_BLACK_LEVEL_CALIBRATION 0x62 +#define MT9P031_BLC_MANUAL_BLC (1 << 0) +#define MT9P031_RED_OFFSET 0x63 +#define MT9P031_BLUE_OFFSET 0x64 +#define MT9P031_TEST_PATTERN 0xa0 +#define MT9P031_TEST_PATTERN_SHIFT 3 +#define MT9P031_TEST_PATTERN_ENABLE (1 << 0) +#define MT9P031_TEST_PATTERN_DISABLE (0 << 0) +#define MT9P031_TEST_PATTERN_GREEN 0xa1 +#define MT9P031_TEST_PATTERN_RED 0xa2 +#define MT9P031_TEST_PATTERN_BLUE 0xa3 + +enum mt9p031_model { + MT9P031_MODEL_COLOR, + MT9P031_MODEL_MONOCHROME, +}; + +struct mt9p031 { + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_rect crop; /* Sensor window */ + struct v4l2_mbus_framefmt format; + struct mt9p031_platform_data *pdata; + struct mutex power_lock; /* lock to protect power_count */ + int power_count; + + enum mt9p031_model model; + struct aptina_pll pll; + int reset; + + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *blc_auto; + struct v4l2_ctrl *blc_offset; + + /* Registers cache */ + u16 output_control; + u16 mode2; +}; + +static struct mt9p031 *to_mt9p031(struct v4l2_subdev *sd) +{ + return container_of(sd, struct mt9p031, subdev); +} + +static int mt9p031_read(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_word_swapped(client, reg); +} + +static int mt9p031_write(struct i2c_client *client, u8 reg, u16 data) +{ + return i2c_smbus_write_word_swapped(client, reg, data); +} + +static int mt9p031_set_output_control(struct mt9p031 *mt9p031, u16 clear, + u16 set) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + u16 value = (mt9p031->output_control & ~clear) | set; + int ret; + + ret = mt9p031_write(client, MT9P031_OUTPUT_CONTROL, value); + if (ret < 0) + return ret; + + mt9p031->output_control = value; + return 0; +} + +static int mt9p031_set_mode2(struct mt9p031 *mt9p031, u16 clear, u16 set) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + u16 value = (mt9p031->mode2 & ~clear) | set; + int ret; + + ret = mt9p031_write(client, MT9P031_READ_MODE_2, value); + if (ret < 0) + return ret; + + mt9p031->mode2 = value; + return 0; +} + +static int mt9p031_reset(struct mt9p031 *mt9p031) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + int ret; + + /* Disable chip output, synchronous option update */ + ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_ENABLE); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_DISABLE); + if (ret < 0) + return ret; + + return mt9p031_set_output_control(mt9p031, MT9P031_OUTPUT_CONTROL_CEN, + 0); +} + +static int mt9p031_pll_setup(struct mt9p031 *mt9p031) +{ + static const struct aptina_pll_limits limits = { + .ext_clock_min = 6000000, + .ext_clock_max = 27000000, + .int_clock_min = 2000000, + .int_clock_max = 13500000, + .out_clock_min = 180000000, + .out_clock_max = 360000000, + .pix_clock_max = 96000000, + .n_min = 1, + .n_max = 64, + .m_min = 16, + .m_max = 255, + .p1_min = 1, + .p1_max = 128, + }; + + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + struct mt9p031_platform_data *pdata = mt9p031->pdata; + + mt9p031->pll.ext_clock = pdata->ext_freq; + mt9p031->pll.pix_clock = pdata->target_freq; + + return aptina_pll_calculate(&client->dev, &limits, &mt9p031->pll); +} + +static int mt9p031_pll_enable(struct mt9p031 *mt9p031) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + int ret; + + ret = mt9p031_write(client, MT9P031_PLL_CONTROL, + MT9P031_PLL_CONTROL_PWRON); + if (ret < 0) + return ret; + + ret = mt9p031_write(client, MT9P031_PLL_CONFIG_1, + (mt9p031->pll.m << 8) | (mt9p031->pll.n - 1)); + if (ret < 0) + return ret; + + ret = mt9p031_write(client, MT9P031_PLL_CONFIG_2, mt9p031->pll.p1 - 1); + if (ret < 0) + return ret; + + usleep_range(1000, 2000); + ret = mt9p031_write(client, MT9P031_PLL_CONTROL, + MT9P031_PLL_CONTROL_PWRON | + MT9P031_PLL_CONTROL_USEPLL); + return ret; +} + +static inline int mt9p031_pll_disable(struct mt9p031 *mt9p031) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + + return mt9p031_write(client, MT9P031_PLL_CONTROL, + MT9P031_PLL_CONTROL_PWROFF); +} + +static int mt9p031_power_on(struct mt9p031 *mt9p031) +{ + /* Ensure RESET_BAR is low */ + if (mt9p031->reset != -1) { + gpio_set_value(mt9p031->reset, 0); + usleep_range(1000, 2000); + } + + /* Emable clock */ + if (mt9p031->pdata->set_xclk) + mt9p031->pdata->set_xclk(&mt9p031->subdev, + mt9p031->pdata->ext_freq); + + /* Now RESET_BAR must be high */ + if (mt9p031->reset != -1) { + gpio_set_value(mt9p031->reset, 1); + usleep_range(1000, 2000); + } + + return 0; +} + +static void mt9p031_power_off(struct mt9p031 *mt9p031) +{ + if (mt9p031->reset != -1) { + gpio_set_value(mt9p031->reset, 0); + usleep_range(1000, 2000); + } + + if (mt9p031->pdata->set_xclk) + mt9p031->pdata->set_xclk(&mt9p031->subdev, 0); +} + +static int __mt9p031_set_power(struct mt9p031 *mt9p031, bool on) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + int ret; + + if (!on) { + mt9p031_power_off(mt9p031); + return 0; + } + + ret = mt9p031_power_on(mt9p031); + if (ret < 0) + return ret; + + ret = mt9p031_reset(mt9p031); + if (ret < 0) { + dev_err(&client->dev, "Failed to reset the camera\n"); + return ret; + } + + return v4l2_ctrl_handler_setup(&mt9p031->ctrls); +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev video operations + */ + +static int mt9p031_set_params(struct mt9p031 *mt9p031) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + struct v4l2_mbus_framefmt *format = &mt9p031->format; + const struct v4l2_rect *crop = &mt9p031->crop; + unsigned int hblank; + unsigned int vblank; + unsigned int xskip; + unsigned int yskip; + unsigned int xbin; + unsigned int ybin; + int ret; + + /* Windows position and size. + * + * TODO: Make sure the start coordinates and window size match the + * skipping, binning and mirroring (see description of registers 2 and 4 + * in table 13, and Binning section on page 41). + */ + ret = mt9p031_write(client, MT9P031_COLUMN_START, crop->left); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_ROW_START, crop->top); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_WINDOW_WIDTH, crop->width - 1); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_WINDOW_HEIGHT, crop->height - 1); + if (ret < 0) + return ret; + + /* Row and column binning and skipping. Use the maximum binning value + * compatible with the skipping settings. + */ + xskip = DIV_ROUND_CLOSEST(crop->width, format->width); + yskip = DIV_ROUND_CLOSEST(crop->height, format->height); + xbin = 1 << (ffs(xskip) - 1); + ybin = 1 << (ffs(yskip) - 1); + + ret = mt9p031_write(client, MT9P031_COLUMN_ADDRESS_MODE, + ((xbin - 1) << 4) | (xskip - 1)); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_ROW_ADDRESS_MODE, + ((ybin - 1) << 4) | (yskip - 1)); + if (ret < 0) + return ret; + + /* Blanking - use minimum value for horizontal blanking and default + * value for vertical blanking. + */ + hblank = 346 * ybin + 64 + (80 >> max_t(unsigned int, xbin, 3)); + vblank = MT9P031_VERTICAL_BLANK_DEF; + + ret = mt9p031_write(client, MT9P031_HORIZONTAL_BLANK, hblank); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_VERTICAL_BLANK, vblank); + if (ret < 0) + return ret; + + return ret; +} + +static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + int ret; + + if (!enable) { + /* Stop sensor readout */ + ret = mt9p031_set_output_control(mt9p031, + MT9P031_OUTPUT_CONTROL_CEN, 0); + if (ret < 0) + return ret; + + return mt9p031_pll_disable(mt9p031); + } + + ret = mt9p031_set_params(mt9p031); + if (ret < 0) + return ret; + + /* Switch to master "normal" mode */ + ret = mt9p031_set_output_control(mt9p031, 0, + MT9P031_OUTPUT_CONTROL_CEN); + if (ret < 0) + return ret; + + return mt9p031_pll_enable(mt9p031); +} + +static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + + if (code->pad || code->index) + return -EINVAL; + + code->code = mt9p031->format.code; + return 0; +} + +static int mt9p031_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + + if (fse->index >= 8 || fse->code != mt9p031->format.code) + return -EINVAL; + + fse->min_width = MT9P031_WINDOW_WIDTH_DEF + / min_t(unsigned int, 7, fse->index + 1); + fse->max_width = fse->min_width; + fse->min_height = MT9P031_WINDOW_HEIGHT_DEF / (fse->index + 1); + fse->max_height = fse->min_height; + + return 0; +} + +static struct v4l2_mbus_framefmt * +__mt9p031_get_pad_format(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(fh, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &mt9p031->format; + default: + return NULL; + } +} + +static struct v4l2_rect * +__mt9p031_get_pad_crop(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(fh, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &mt9p031->crop; + default: + return NULL; + } +} + +static int mt9p031_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + + fmt->format = *__mt9p031_get_pad_format(mt9p031, fh, fmt->pad, + fmt->which); + return 0; +} + +static int mt9p031_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + struct v4l2_mbus_framefmt *__format; + struct v4l2_rect *__crop; + unsigned int width; + unsigned int height; + unsigned int hratio; + unsigned int vratio; + + __crop = __mt9p031_get_pad_crop(mt9p031, fh, format->pad, + format->which); + + /* Clamp the width and height to avoid dividing by zero. */ + width = clamp_t(unsigned int, ALIGN(format->format.width, 2), + max(__crop->width / 7, MT9P031_WINDOW_WIDTH_MIN), + __crop->width); + height = clamp_t(unsigned int, ALIGN(format->format.height, 2), + max(__crop->height / 8, MT9P031_WINDOW_HEIGHT_MIN), + __crop->height); + + hratio = DIV_ROUND_CLOSEST(__crop->width, width); + vratio = DIV_ROUND_CLOSEST(__crop->height, height); + + __format = __mt9p031_get_pad_format(mt9p031, fh, format->pad, + format->which); + __format->width = __crop->width / hratio; + __format->height = __crop->height / vratio; + + format->format = *__format; + + return 0; +} + +static int mt9p031_get_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + + crop->rect = *__mt9p031_get_pad_crop(mt9p031, fh, crop->pad, + crop->which); + return 0; +} + +static int mt9p031_set_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + struct v4l2_mbus_framefmt *__format; + struct v4l2_rect *__crop; + struct v4l2_rect rect; + + /* Clamp the crop rectangle boundaries and align them to a multiple of 2 + * pixels to ensure a GRBG Bayer pattern. + */ + rect.left = clamp(ALIGN(crop->rect.left, 2), MT9P031_COLUMN_START_MIN, + MT9P031_COLUMN_START_MAX); + rect.top = clamp(ALIGN(crop->rect.top, 2), MT9P031_ROW_START_MIN, + MT9P031_ROW_START_MAX); + rect.width = clamp(ALIGN(crop->rect.width, 2), + MT9P031_WINDOW_WIDTH_MIN, + MT9P031_WINDOW_WIDTH_MAX); + rect.height = clamp(ALIGN(crop->rect.height, 2), + MT9P031_WINDOW_HEIGHT_MIN, + MT9P031_WINDOW_HEIGHT_MAX); + + rect.width = min(rect.width, MT9P031_PIXEL_ARRAY_WIDTH - rect.left); + rect.height = min(rect.height, MT9P031_PIXEL_ARRAY_HEIGHT - rect.top); + + __crop = __mt9p031_get_pad_crop(mt9p031, fh, crop->pad, crop->which); + + if (rect.width != __crop->width || rect.height != __crop->height) { + /* Reset the output image size if the crop rectangle size has + * been modified. + */ + __format = __mt9p031_get_pad_format(mt9p031, fh, crop->pad, + crop->which); + __format->width = rect.width; + __format->height = rect.height; + } + + *__crop = rect; + crop->rect = rect; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev control operations + */ + +#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001) +#define V4L2_CID_BLC_AUTO (V4L2_CID_USER_BASE | 0x1002) +#define V4L2_CID_BLC_TARGET_LEVEL (V4L2_CID_USER_BASE | 0x1003) +#define V4L2_CID_BLC_ANALOG_OFFSET (V4L2_CID_USER_BASE | 0x1004) +#define V4L2_CID_BLC_DIGITAL_OFFSET (V4L2_CID_USER_BASE | 0x1005) + +static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9p031 *mt9p031 = + container_of(ctrl->handler, struct mt9p031, ctrls); + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + u16 data; + int ret; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + ret = mt9p031_write(client, MT9P031_SHUTTER_WIDTH_UPPER, + (ctrl->val >> 16) & 0xffff); + if (ret < 0) + return ret; + + return mt9p031_write(client, MT9P031_SHUTTER_WIDTH_LOWER, + ctrl->val & 0xffff); + + case V4L2_CID_GAIN: + /* Gain is controlled by 2 analog stages and a digital stage. + * Valid values for the 3 stages are + * + * Stage Min Max Step + * ------------------------------------------ + * First analog stage x1 x2 1 + * Second analog stage x1 x4 0.125 + * Digital stage x1 x16 0.125 + * + * To minimize noise, the gain stages should be used in the + * second analog stage, first analog stage, digital stage order. + * Gain from a previous stage should be pushed to its maximum + * value before the next stage is used. + */ + if (ctrl->val <= 32) { + data = ctrl->val; + } else if (ctrl->val <= 64) { + ctrl->val &= ~1; + data = (1 << 6) | (ctrl->val >> 1); + } else { + ctrl->val &= ~7; + data = ((ctrl->val - 64) << 5) | (1 << 6) | 32; + } + + return mt9p031_write(client, MT9P031_GLOBAL_GAIN, data); + + case V4L2_CID_HFLIP: + if (ctrl->val) + return mt9p031_set_mode2(mt9p031, + 0, MT9P031_READ_MODE_2_COL_MIR); + else + return mt9p031_set_mode2(mt9p031, + MT9P031_READ_MODE_2_COL_MIR, 0); + + case V4L2_CID_VFLIP: + if (ctrl->val) + return mt9p031_set_mode2(mt9p031, + 0, MT9P031_READ_MODE_2_ROW_MIR); + else + return mt9p031_set_mode2(mt9p031, + MT9P031_READ_MODE_2_ROW_MIR, 0); + + case V4L2_CID_TEST_PATTERN: + if (!ctrl->val) { + /* Restore the black level compensation settings. */ + if (mt9p031->blc_auto->cur.val != 0) { + ret = mt9p031_s_ctrl(mt9p031->blc_auto); + if (ret < 0) + return ret; + } + if (mt9p031->blc_offset->cur.val != 0) { + ret = mt9p031_s_ctrl(mt9p031->blc_offset); + if (ret < 0) + return ret; + } + return mt9p031_write(client, MT9P031_TEST_PATTERN, + MT9P031_TEST_PATTERN_DISABLE); + } + + ret = mt9p031_write(client, MT9P031_TEST_PATTERN_GREEN, 0x05a0); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_TEST_PATTERN_RED, 0x0a50); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_TEST_PATTERN_BLUE, 0x0aa0); + if (ret < 0) + return ret; + + /* Disable digital black level compensation when using a test + * pattern. + */ + ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC, + 0); + if (ret < 0) + return ret; + + ret = mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, 0); + if (ret < 0) + return ret; + + return mt9p031_write(client, MT9P031_TEST_PATTERN, + ((ctrl->val - 1) << MT9P031_TEST_PATTERN_SHIFT) + | MT9P031_TEST_PATTERN_ENABLE); + + case V4L2_CID_BLC_AUTO: + ret = mt9p031_set_mode2(mt9p031, + ctrl->val ? 0 : MT9P031_READ_MODE_2_ROW_BLC, + ctrl->val ? MT9P031_READ_MODE_2_ROW_BLC : 0); + if (ret < 0) + return ret; + + return mt9p031_write(client, MT9P031_BLACK_LEVEL_CALIBRATION, + ctrl->val ? 0 : MT9P031_BLC_MANUAL_BLC); + + case V4L2_CID_BLC_TARGET_LEVEL: + return mt9p031_write(client, MT9P031_ROW_BLACK_TARGET, + ctrl->val); + + case V4L2_CID_BLC_ANALOG_OFFSET: + data = ctrl->val & ((1 << 9) - 1); + + ret = mt9p031_write(client, MT9P031_GREEN1_OFFSET, data); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_GREEN2_OFFSET, data); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_RED_OFFSET, data); + if (ret < 0) + return ret; + return mt9p031_write(client, MT9P031_BLUE_OFFSET, data); + + case V4L2_CID_BLC_DIGITAL_OFFSET: + return mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, + ctrl->val & ((1 << 12) - 1)); + } + + return 0; +} + +static struct v4l2_ctrl_ops mt9p031_ctrl_ops = { + .s_ctrl = mt9p031_s_ctrl, +}; + +static const char * const mt9p031_test_pattern_menu[] = { + "Disabled", + "Color Field", + "Horizontal Gradient", + "Vertical Gradient", + "Diagonal Gradient", + "Classic Test Pattern", + "Walking 1s", + "Monochrome Horizontal Bars", + "Monochrome Vertical Bars", + "Vertical Color Bars", +}; + +static const struct v4l2_ctrl_config mt9p031_ctrls[] = { + { + .ops = &mt9p031_ctrl_ops, + .id = V4L2_CID_TEST_PATTERN, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Test Pattern", + .min = 0, + .max = ARRAY_SIZE(mt9p031_test_pattern_menu) - 1, + .step = 0, + .def = 0, + .flags = 0, + .menu_skip_mask = 0, + .qmenu = mt9p031_test_pattern_menu, + }, { + .ops = &mt9p031_ctrl_ops, + .id = V4L2_CID_BLC_AUTO, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "BLC, Auto", + .min = 0, + .max = 1, + .step = 1, + .def = 1, + .flags = 0, + }, { + .ops = &mt9p031_ctrl_ops, + .id = V4L2_CID_BLC_TARGET_LEVEL, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "BLC Target Level", + .min = 0, + .max = 4095, + .step = 1, + .def = 168, + .flags = 0, + }, { + .ops = &mt9p031_ctrl_ops, + .id = V4L2_CID_BLC_ANALOG_OFFSET, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "BLC Analog Offset", + .min = -255, + .max = 255, + .step = 1, + .def = 32, + .flags = 0, + }, { + .ops = &mt9p031_ctrl_ops, + .id = V4L2_CID_BLC_DIGITAL_OFFSET, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "BLC Digital Offset", + .min = -2048, + .max = 2047, + .step = 1, + .def = 40, + .flags = 0, + } +}; + +/* ----------------------------------------------------------------------------- + * V4L2 subdev core operations + */ + +static int mt9p031_set_power(struct v4l2_subdev *subdev, int on) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + int ret = 0; + + mutex_lock(&mt9p031->power_lock); + + /* If the power count is modified from 0 to != 0 or from != 0 to 0, + * update the power state. + */ + if (mt9p031->power_count == !on) { + ret = __mt9p031_set_power(mt9p031, !!on); + if (ret < 0) + goto out; + } + + /* Update the power count. */ + mt9p031->power_count += on ? 1 : -1; + WARN_ON(mt9p031->power_count < 0); + +out: + mutex_unlock(&mt9p031->power_lock); + return ret; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev internal operations + */ + +static int mt9p031_registered(struct v4l2_subdev *subdev) +{ + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + s32 data; + int ret; + + ret = mt9p031_power_on(mt9p031); + if (ret < 0) { + dev_err(&client->dev, "MT9P031 power up failed\n"); + return ret; + } + + /* Read out the chip version register */ + data = mt9p031_read(client, MT9P031_CHIP_VERSION); + if (data != MT9P031_CHIP_VERSION_VALUE) { + dev_err(&client->dev, "MT9P031 not detected, wrong version " + "0x%04x\n", data); + return -ENODEV; + } + + mt9p031_power_off(mt9p031); + + dev_info(&client->dev, "MT9P031 detected at address 0x%02x\n", + client->addr); + + return ret; +} + +static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + crop = v4l2_subdev_get_try_crop(fh, 0); + crop->left = MT9P031_COLUMN_START_DEF; + crop->top = MT9P031_ROW_START_DEF; + crop->width = MT9P031_WINDOW_WIDTH_DEF; + crop->height = MT9P031_WINDOW_HEIGHT_DEF; + + format = v4l2_subdev_get_try_format(fh, 0); + + if (mt9p031->model == MT9P031_MODEL_MONOCHROME) + format->code = V4L2_MBUS_FMT_Y12_1X12; + else + format->code = V4L2_MBUS_FMT_SGRBG12_1X12; + + format->width = MT9P031_WINDOW_WIDTH_DEF; + format->height = MT9P031_WINDOW_HEIGHT_DEF; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + return mt9p031_set_power(subdev, 1); +} + +static int mt9p031_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + return mt9p031_set_power(subdev, 0); +} + +static struct v4l2_subdev_core_ops mt9p031_subdev_core_ops = { + .s_power = mt9p031_set_power, +}; + +static struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = { + .s_stream = mt9p031_s_stream, +}; + +static struct v4l2_subdev_pad_ops mt9p031_subdev_pad_ops = { + .enum_mbus_code = mt9p031_enum_mbus_code, + .enum_frame_size = mt9p031_enum_frame_size, + .get_fmt = mt9p031_get_format, + .set_fmt = mt9p031_set_format, + .get_crop = mt9p031_get_crop, + .set_crop = mt9p031_set_crop, +}; + +static struct v4l2_subdev_ops mt9p031_subdev_ops = { + .core = &mt9p031_subdev_core_ops, + .video = &mt9p031_subdev_video_ops, + .pad = &mt9p031_subdev_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops mt9p031_subdev_internal_ops = { + .registered = mt9p031_registered, + .open = mt9p031_open, + .close = mt9p031_close, +}; + +/* ----------------------------------------------------------------------------- + * Driver initialization and probing + */ + +static int mt9p031_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct mt9p031_platform_data *pdata = client->dev.platform_data; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct mt9p031 *mt9p031; + unsigned int i; + int ret; + + if (pdata == NULL) { + dev_err(&client->dev, "No platform data\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { + dev_warn(&client->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + mt9p031 = kzalloc(sizeof(*mt9p031), GFP_KERNEL); + if (mt9p031 == NULL) + return -ENOMEM; + + mt9p031->pdata = pdata; + mt9p031->output_control = MT9P031_OUTPUT_CONTROL_DEF; + mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC; + mt9p031->model = did->driver_data; + mt9p031->reset = -1; + + v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 5); + + v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, + V4L2_CID_EXPOSURE, MT9P031_SHUTTER_WIDTH_MIN, + MT9P031_SHUTTER_WIDTH_MAX, 1, + MT9P031_SHUTTER_WIDTH_DEF); + v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, + V4L2_CID_GAIN, MT9P031_GLOBAL_GAIN_MIN, + MT9P031_GLOBAL_GAIN_MAX, 1, MT9P031_GLOBAL_GAIN_DEF); + v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, + V4L2_CID_PIXEL_RATE, pdata->target_freq, + pdata->target_freq, 1, pdata->target_freq); + + for (i = 0; i < ARRAY_SIZE(mt9p031_ctrls); ++i) + v4l2_ctrl_new_custom(&mt9p031->ctrls, &mt9p031_ctrls[i], NULL); + + mt9p031->subdev.ctrl_handler = &mt9p031->ctrls; + + if (mt9p031->ctrls.error) { + printk(KERN_INFO "%s: control initialization error %d\n", + __func__, mt9p031->ctrls.error); + ret = mt9p031->ctrls.error; + goto done; + } + + mt9p031->blc_auto = v4l2_ctrl_find(&mt9p031->ctrls, V4L2_CID_BLC_AUTO); + mt9p031->blc_offset = v4l2_ctrl_find(&mt9p031->ctrls, + V4L2_CID_BLC_DIGITAL_OFFSET); + + mutex_init(&mt9p031->power_lock); + v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops); + mt9p031->subdev.internal_ops = &mt9p031_subdev_internal_ops; + + mt9p031->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&mt9p031->subdev.entity, 1, &mt9p031->pad, 0); + if (ret < 0) + goto done; + + mt9p031->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + mt9p031->crop.width = MT9P031_WINDOW_WIDTH_DEF; + mt9p031->crop.height = MT9P031_WINDOW_HEIGHT_DEF; + mt9p031->crop.left = MT9P031_COLUMN_START_DEF; + mt9p031->crop.top = MT9P031_ROW_START_DEF; + + if (mt9p031->model == MT9P031_MODEL_MONOCHROME) + mt9p031->format.code = V4L2_MBUS_FMT_Y12_1X12; + else + mt9p031->format.code = V4L2_MBUS_FMT_SGRBG12_1X12; + + mt9p031->format.width = MT9P031_WINDOW_WIDTH_DEF; + mt9p031->format.height = MT9P031_WINDOW_HEIGHT_DEF; + mt9p031->format.field = V4L2_FIELD_NONE; + mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB; + + if (pdata->reset != -1) { + ret = gpio_request_one(pdata->reset, GPIOF_OUT_INIT_LOW, + "mt9p031_rst"); + if (ret < 0) + goto done; + + mt9p031->reset = pdata->reset; + } + + ret = mt9p031_pll_setup(mt9p031); + +done: + if (ret < 0) { + if (mt9p031->reset != -1) + gpio_free(mt9p031->reset); + + v4l2_ctrl_handler_free(&mt9p031->ctrls); + media_entity_cleanup(&mt9p031->subdev.entity); + kfree(mt9p031); + } + + return ret; +} + +static int mt9p031_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + + v4l2_ctrl_handler_free(&mt9p031->ctrls); + v4l2_device_unregister_subdev(subdev); + media_entity_cleanup(&subdev->entity); + if (mt9p031->reset != -1) + gpio_free(mt9p031->reset); + kfree(mt9p031); + + return 0; +} + +static const struct i2c_device_id mt9p031_id[] = { + { "mt9p031", MT9P031_MODEL_COLOR }, + { "mt9p031m", MT9P031_MODEL_MONOCHROME }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mt9p031_id); + +static struct i2c_driver mt9p031_i2c_driver = { + .driver = { + .name = "mt9p031", + }, + .probe = mt9p031_probe, + .remove = mt9p031_remove, + .id_table = mt9p031_id, +}; + +module_i2c_driver(mt9p031_i2c_driver); + +MODULE_DESCRIPTION("Aptina MT9P031 Camera driver"); +MODULE_AUTHOR("Bastian Hecht "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/mt9t001.c b/drivers/media/i2c/mt9t001.c new file mode 100644 index 00000000000..6d343adf891 --- /dev/null +++ b/drivers/media/i2c/mt9t001.c @@ -0,0 +1,833 @@ +/* + * Driver for MT9T001 CMOS Image Sensor from Aptina (Micron) + * + * Copyright (C) 2010-2011, Laurent Pinchart + * + * Based on the MT9M001 driver, + * + * Copyright (C) 2008, Guennadi Liakhovetski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define MT9T001_PIXEL_ARRAY_HEIGHT 1568 +#define MT9T001_PIXEL_ARRAY_WIDTH 2112 + +#define MT9T001_CHIP_VERSION 0x00 +#define MT9T001_CHIP_ID 0x1621 +#define MT9T001_ROW_START 0x01 +#define MT9T001_ROW_START_MIN 0 +#define MT9T001_ROW_START_DEF 20 +#define MT9T001_ROW_START_MAX 1534 +#define MT9T001_COLUMN_START 0x02 +#define MT9T001_COLUMN_START_MIN 0 +#define MT9T001_COLUMN_START_DEF 32 +#define MT9T001_COLUMN_START_MAX 2046 +#define MT9T001_WINDOW_HEIGHT 0x03 +#define MT9T001_WINDOW_HEIGHT_MIN 1 +#define MT9T001_WINDOW_HEIGHT_DEF 1535 +#define MT9T001_WINDOW_HEIGHT_MAX 1567 +#define MT9T001_WINDOW_WIDTH 0x04 +#define MT9T001_WINDOW_WIDTH_MIN 1 +#define MT9T001_WINDOW_WIDTH_DEF 2047 +#define MT9T001_WINDOW_WIDTH_MAX 2111 +#define MT9T001_HORIZONTAL_BLANKING 0x05 +#define MT9T001_HORIZONTAL_BLANKING_MIN 21 +#define MT9T001_HORIZONTAL_BLANKING_MAX 1023 +#define MT9T001_VERTICAL_BLANKING 0x06 +#define MT9T001_VERTICAL_BLANKING_MIN 3 +#define MT9T001_VERTICAL_BLANKING_MAX 1023 +#define MT9T001_OUTPUT_CONTROL 0x07 +#define MT9T001_OUTPUT_CONTROL_SYNC (1 << 0) +#define MT9T001_OUTPUT_CONTROL_CHIP_ENABLE (1 << 1) +#define MT9T001_OUTPUT_CONTROL_TEST_DATA (1 << 6) +#define MT9T001_SHUTTER_WIDTH_HIGH 0x08 +#define MT9T001_SHUTTER_WIDTH_LOW 0x09 +#define MT9T001_SHUTTER_WIDTH_MIN 1 +#define MT9T001_SHUTTER_WIDTH_DEF 1561 +#define MT9T001_SHUTTER_WIDTH_MAX (1024 * 1024) +#define MT9T001_PIXEL_CLOCK 0x0a +#define MT9T001_PIXEL_CLOCK_INVERT (1 << 15) +#define MT9T001_PIXEL_CLOCK_SHIFT_MASK (7 << 8) +#define MT9T001_PIXEL_CLOCK_SHIFT_SHIFT 8 +#define MT9T001_PIXEL_CLOCK_DIVIDE_MASK (0x7f << 0) +#define MT9T001_FRAME_RESTART 0x0b +#define MT9T001_SHUTTER_DELAY 0x0c +#define MT9T001_SHUTTER_DELAY_MAX 2047 +#define MT9T001_RESET 0x0d +#define MT9T001_READ_MODE1 0x1e +#define MT9T001_READ_MODE_SNAPSHOT (1 << 8) +#define MT9T001_READ_MODE_STROBE_ENABLE (1 << 9) +#define MT9T001_READ_MODE_STROBE_WIDTH (1 << 10) +#define MT9T001_READ_MODE_STROBE_OVERRIDE (1 << 11) +#define MT9T001_READ_MODE2 0x20 +#define MT9T001_READ_MODE_BAD_FRAMES (1 << 0) +#define MT9T001_READ_MODE_LINE_VALID_CONTINUOUS (1 << 9) +#define MT9T001_READ_MODE_LINE_VALID_FRAME (1 << 10) +#define MT9T001_READ_MODE3 0x21 +#define MT9T001_READ_MODE_GLOBAL_RESET (1 << 0) +#define MT9T001_READ_MODE_GHST_CTL (1 << 1) +#define MT9T001_ROW_ADDRESS_MODE 0x22 +#define MT9T001_ROW_SKIP_MASK (7 << 0) +#define MT9T001_ROW_BIN_MASK (3 << 3) +#define MT9T001_ROW_BIN_SHIFT 3 +#define MT9T001_COLUMN_ADDRESS_MODE 0x23 +#define MT9T001_COLUMN_SKIP_MASK (7 << 0) +#define MT9T001_COLUMN_BIN_MASK (3 << 3) +#define MT9T001_COLUMN_BIN_SHIFT 3 +#define MT9T001_GREEN1_GAIN 0x2b +#define MT9T001_BLUE_GAIN 0x2c +#define MT9T001_RED_GAIN 0x2d +#define MT9T001_GREEN2_GAIN 0x2e +#define MT9T001_TEST_DATA 0x32 +#define MT9T001_GLOBAL_GAIN 0x35 +#define MT9T001_GLOBAL_GAIN_MIN 8 +#define MT9T001_GLOBAL_GAIN_MAX 1024 +#define MT9T001_BLACK_LEVEL 0x49 +#define MT9T001_ROW_BLACK_DEFAULT_OFFSET 0x4b +#define MT9T001_BLC_DELTA_THRESHOLDS 0x5d +#define MT9T001_CAL_THRESHOLDS 0x5f +#define MT9T001_GREEN1_OFFSET 0x60 +#define MT9T001_GREEN2_OFFSET 0x61 +#define MT9T001_BLACK_LEVEL_CALIBRATION 0x62 +#define MT9T001_BLACK_LEVEL_OVERRIDE (1 << 0) +#define MT9T001_BLACK_LEVEL_DISABLE_OFFSET (1 << 1) +#define MT9T001_BLACK_LEVEL_RECALCULATE (1 << 12) +#define MT9T001_BLACK_LEVEL_LOCK_RED_BLUE (1 << 13) +#define MT9T001_BLACK_LEVEL_LOCK_GREEN (1 << 14) +#define MT9T001_RED_OFFSET 0x63 +#define MT9T001_BLUE_OFFSET 0x64 + +struct mt9t001 { + struct v4l2_subdev subdev; + struct media_pad pad; + + struct v4l2_mbus_framefmt format; + struct v4l2_rect crop; + + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *gains[4]; + + u16 output_control; + u16 black_level; +}; + +static inline struct mt9t001 *to_mt9t001(struct v4l2_subdev *sd) +{ + return container_of(sd, struct mt9t001, subdev); +} + +static int mt9t001_read(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_word_swapped(client, reg); +} + +static int mt9t001_write(struct i2c_client *client, u8 reg, u16 data) +{ + return i2c_smbus_write_word_swapped(client, reg, data); +} + +static int mt9t001_set_output_control(struct mt9t001 *mt9t001, u16 clear, + u16 set) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev); + u16 value = (mt9t001->output_control & ~clear) | set; + int ret; + + if (value == mt9t001->output_control) + return 0; + + ret = mt9t001_write(client, MT9T001_OUTPUT_CONTROL, value); + if (ret < 0) + return ret; + + mt9t001->output_control = value; + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev video operations + */ + +static struct v4l2_mbus_framefmt * +__mt9t001_get_pad_format(struct mt9t001 *mt9t001, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(fh, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &mt9t001->format; + default: + return NULL; + } +} + +static struct v4l2_rect * +__mt9t001_get_pad_crop(struct mt9t001 *mt9t001, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(fh, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &mt9t001->crop; + default: + return NULL; + } +} + +static int mt9t001_s_stream(struct v4l2_subdev *subdev, int enable) +{ + const u16 mode = MT9T001_OUTPUT_CONTROL_CHIP_ENABLE; + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct mt9t001 *mt9t001 = to_mt9t001(subdev); + struct v4l2_mbus_framefmt *format = &mt9t001->format; + struct v4l2_rect *crop = &mt9t001->crop; + unsigned int hratio; + unsigned int vratio; + int ret; + + if (!enable) + return mt9t001_set_output_control(mt9t001, mode, 0); + + /* Configure the window size and row/column bin */ + hratio = DIV_ROUND_CLOSEST(crop->width, format->width); + vratio = DIV_ROUND_CLOSEST(crop->height, format->height); + + ret = mt9t001_write(client, MT9T001_ROW_ADDRESS_MODE, hratio - 1); + if (ret < 0) + return ret; + + ret = mt9t001_write(client, MT9T001_COLUMN_ADDRESS_MODE, vratio - 1); + if (ret < 0) + return ret; + + ret = mt9t001_write(client, MT9T001_COLUMN_START, crop->left); + if (ret < 0) + return ret; + + ret = mt9t001_write(client, MT9T001_ROW_START, crop->top); + if (ret < 0) + return ret; + + ret = mt9t001_write(client, MT9T001_WINDOW_WIDTH, crop->width - 1); + if (ret < 0) + return ret; + + ret = mt9t001_write(client, MT9T001_WINDOW_HEIGHT, crop->height - 1); + if (ret < 0) + return ret; + + /* Switch to master "normal" mode */ + return mt9t001_set_output_control(mt9t001, 0, mode); +} + +static int mt9t001_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = V4L2_MBUS_FMT_SGRBG10_1X10; + return 0; +} + +static int mt9t001_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= 8 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10) + return -EINVAL; + + fse->min_width = (MT9T001_WINDOW_WIDTH_DEF + 1) / fse->index; + fse->max_width = fse->min_width; + fse->min_height = (MT9T001_WINDOW_HEIGHT_DEF + 1) / fse->index; + fse->max_height = fse->min_height; + + return 0; +} + +static int mt9t001_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format) +{ + struct mt9t001 *mt9t001 = to_mt9t001(subdev); + + format->format = *__mt9t001_get_pad_format(mt9t001, fh, format->pad, + format->which); + return 0; +} + +static int mt9t001_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format) +{ + struct mt9t001 *mt9t001 = to_mt9t001(subdev); + struct v4l2_mbus_framefmt *__format; + struct v4l2_rect *__crop; + unsigned int width; + unsigned int height; + unsigned int hratio; + unsigned int vratio; + + __crop = __mt9t001_get_pad_crop(mt9t001, fh, format->pad, + format->which); + + /* Clamp the width and height to avoid dividing by zero. */ + width = clamp_t(unsigned int, ALIGN(format->format.width, 2), + max(__crop->width / 8, MT9T001_WINDOW_HEIGHT_MIN + 1), + __crop->width); + height = clamp_t(unsigned int, ALIGN(format->format.height, 2), + max(__crop->height / 8, MT9T001_WINDOW_HEIGHT_MIN + 1), + __crop->height); + + hratio = DIV_ROUND_CLOSEST(__crop->width, width); + vratio = DIV_ROUND_CLOSEST(__crop->height, height); + + __format = __mt9t001_get_pad_format(mt9t001, fh, format->pad, + format->which); + __format->width = __crop->width / hratio; + __format->height = __crop->height / vratio; + + format->format = *__format; + + return 0; +} + +static int mt9t001_get_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct mt9t001 *mt9t001 = to_mt9t001(subdev); + + crop->rect = *__mt9t001_get_pad_crop(mt9t001, fh, crop->pad, + crop->which); + return 0; +} + +static int mt9t001_set_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct mt9t001 *mt9t001 = to_mt9t001(subdev); + struct v4l2_mbus_framefmt *__format; + struct v4l2_rect *__crop; + struct v4l2_rect rect; + + /* Clamp the crop rectangle boundaries and align them to a multiple of 2 + * pixels. + */ + rect.left = clamp(ALIGN(crop->rect.left, 2), + MT9T001_COLUMN_START_MIN, + MT9T001_COLUMN_START_MAX); + rect.top = clamp(ALIGN(crop->rect.top, 2), + MT9T001_ROW_START_MIN, + MT9T001_ROW_START_MAX); + rect.width = clamp(ALIGN(crop->rect.width, 2), + MT9T001_WINDOW_WIDTH_MIN + 1, + MT9T001_WINDOW_WIDTH_MAX + 1); + rect.height = clamp(ALIGN(crop->rect.height, 2), + MT9T001_WINDOW_HEIGHT_MIN + 1, + MT9T001_WINDOW_HEIGHT_MAX + 1); + + rect.width = min(rect.width, MT9T001_PIXEL_ARRAY_WIDTH - rect.left); + rect.height = min(rect.height, MT9T001_PIXEL_ARRAY_HEIGHT - rect.top); + + __crop = __mt9t001_get_pad_crop(mt9t001, fh, crop->pad, crop->which); + + if (rect.width != __crop->width || rect.height != __crop->height) { + /* Reset the output image size if the crop rectangle size has + * been modified. + */ + __format = __mt9t001_get_pad_format(mt9t001, fh, crop->pad, + crop->which); + __format->width = rect.width; + __format->height = rect.height; + } + + *__crop = rect; + crop->rect = rect; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev control operations + */ + +#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001) +#define V4L2_CID_BLACK_LEVEL_AUTO (V4L2_CID_USER_BASE | 0x1002) +#define V4L2_CID_BLACK_LEVEL_OFFSET (V4L2_CID_USER_BASE | 0x1003) +#define V4L2_CID_BLACK_LEVEL_CALIBRATE (V4L2_CID_USER_BASE | 0x1004) + +#define V4L2_CID_GAIN_RED (V4L2_CTRL_CLASS_CAMERA | 0x1001) +#define V4L2_CID_GAIN_GREEN_RED (V4L2_CTRL_CLASS_CAMERA | 0x1002) +#define V4L2_CID_GAIN_GREEN_BLUE (V4L2_CTRL_CLASS_CAMERA | 0x1003) +#define V4L2_CID_GAIN_BLUE (V4L2_CTRL_CLASS_CAMERA | 0x1004) + +static u16 mt9t001_gain_value(s32 *gain) +{ + /* Gain is controlled by 2 analog stages and a digital stage. Valid + * values for the 3 stages are + * + * Stage Min Max Step + * ------------------------------------------ + * First analog stage x1 x2 1 + * Second analog stage x1 x4 0.125 + * Digital stage x1 x16 0.125 + * + * To minimize noise, the gain stages should be used in the second + * analog stage, first analog stage, digital stage order. Gain from a + * previous stage should be pushed to its maximum value before the next + * stage is used. + */ + if (*gain <= 32) + return *gain; + + if (*gain <= 64) { + *gain &= ~1; + return (1 << 6) | (*gain >> 1); + } + + *gain &= ~7; + return ((*gain - 64) << 5) | (1 << 6) | 32; +} + +static int mt9t001_ctrl_freeze(struct mt9t001 *mt9t001, bool freeze) +{ + return mt9t001_set_output_control(mt9t001, + freeze ? 0 : MT9T001_OUTPUT_CONTROL_SYNC, + freeze ? MT9T001_OUTPUT_CONTROL_SYNC : 0); +} + +static int mt9t001_s_ctrl(struct v4l2_ctrl *ctrl) +{ + static const u8 gains[4] = { + MT9T001_RED_GAIN, MT9T001_GREEN1_GAIN, + MT9T001_GREEN2_GAIN, MT9T001_BLUE_GAIN + }; + + struct mt9t001 *mt9t001 = + container_of(ctrl->handler, struct mt9t001, ctrls); + struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev); + unsigned int count; + unsigned int i; + u16 value; + int ret; + + switch (ctrl->id) { + case V4L2_CID_GAIN_RED: + case V4L2_CID_GAIN_GREEN_RED: + case V4L2_CID_GAIN_GREEN_BLUE: + case V4L2_CID_GAIN_BLUE: + + /* Disable control updates if more than one control has changed + * in the cluster. + */ + for (i = 0, count = 0; i < 4; ++i) { + struct v4l2_ctrl *gain = mt9t001->gains[i]; + + if (gain->val != gain->cur.val) + count++; + } + + if (count > 1) { + ret = mt9t001_ctrl_freeze(mt9t001, true); + if (ret < 0) + return ret; + } + + /* Update the gain controls. */ + for (i = 0; i < 4; ++i) { + struct v4l2_ctrl *gain = mt9t001->gains[i]; + + if (gain->val == gain->cur.val) + continue; + + value = mt9t001_gain_value(&gain->val); + ret = mt9t001_write(client, gains[i], value); + if (ret < 0) { + mt9t001_ctrl_freeze(mt9t001, false); + return ret; + } + } + + /* Enable control updates. */ + if (count > 1) { + ret = mt9t001_ctrl_freeze(mt9t001, false); + if (ret < 0) + return ret; + } + + break; + + case V4L2_CID_EXPOSURE: + ret = mt9t001_write(client, MT9T001_SHUTTER_WIDTH_LOW, + ctrl->val & 0xffff); + if (ret < 0) + return ret; + + return mt9t001_write(client, MT9T001_SHUTTER_WIDTH_HIGH, + ctrl->val >> 16); + + case V4L2_CID_TEST_PATTERN: + ret = mt9t001_set_output_control(mt9t001, + ctrl->val ? 0 : MT9T001_OUTPUT_CONTROL_TEST_DATA, + ctrl->val ? MT9T001_OUTPUT_CONTROL_TEST_DATA : 0); + if (ret < 0) + return ret; + + return mt9t001_write(client, MT9T001_TEST_DATA, ctrl->val << 2); + + case V4L2_CID_BLACK_LEVEL_AUTO: + value = ctrl->val ? 0 : MT9T001_BLACK_LEVEL_OVERRIDE; + ret = mt9t001_write(client, MT9T001_BLACK_LEVEL_CALIBRATION, + value); + if (ret < 0) + return ret; + + mt9t001->black_level = value; + break; + + case V4L2_CID_BLACK_LEVEL_OFFSET: + ret = mt9t001_write(client, MT9T001_GREEN1_OFFSET, ctrl->val); + if (ret < 0) + return ret; + + ret = mt9t001_write(client, MT9T001_GREEN2_OFFSET, ctrl->val); + if (ret < 0) + return ret; + + ret = mt9t001_write(client, MT9T001_RED_OFFSET, ctrl->val); + if (ret < 0) + return ret; + + return mt9t001_write(client, MT9T001_BLUE_OFFSET, ctrl->val); + + case V4L2_CID_BLACK_LEVEL_CALIBRATE: + return mt9t001_write(client, MT9T001_BLACK_LEVEL_CALIBRATION, + MT9T001_BLACK_LEVEL_RECALCULATE | + mt9t001->black_level); + } + + return 0; +} + +static struct v4l2_ctrl_ops mt9t001_ctrl_ops = { + .s_ctrl = mt9t001_s_ctrl, +}; + +static const struct v4l2_ctrl_config mt9t001_ctrls[] = { + { + .ops = &mt9t001_ctrl_ops, + .id = V4L2_CID_TEST_PATTERN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Test pattern", + .min = 0, + .max = 1023, + .step = 1, + .def = 0, + .flags = 0, + }, { + .ops = &mt9t001_ctrl_ops, + .id = V4L2_CID_BLACK_LEVEL_AUTO, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Black Level, Auto", + .min = 0, + .max = 1, + .step = 1, + .def = 1, + .flags = 0, + }, { + .ops = &mt9t001_ctrl_ops, + .id = V4L2_CID_BLACK_LEVEL_OFFSET, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Black Level, Offset", + .min = -256, + .max = 255, + .step = 1, + .def = 32, + .flags = 0, + }, { + .ops = &mt9t001_ctrl_ops, + .id = V4L2_CID_BLACK_LEVEL_CALIBRATE, + .type = V4L2_CTRL_TYPE_BUTTON, + .name = "Black Level, Calibrate", + .min = 0, + .max = 0, + .step = 0, + .def = 0, + .flags = V4L2_CTRL_FLAG_WRITE_ONLY, + }, +}; + +static const struct v4l2_ctrl_config mt9t001_gains[] = { + { + .ops = &mt9t001_ctrl_ops, + .id = V4L2_CID_GAIN_RED, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Red", + .min = MT9T001_GLOBAL_GAIN_MIN, + .max = MT9T001_GLOBAL_GAIN_MAX, + .step = 1, + .def = MT9T001_GLOBAL_GAIN_MIN, + .flags = 0, + }, { + .ops = &mt9t001_ctrl_ops, + .id = V4L2_CID_GAIN_GREEN_RED, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Green (R)", + .min = MT9T001_GLOBAL_GAIN_MIN, + .max = MT9T001_GLOBAL_GAIN_MAX, + .step = 1, + .def = MT9T001_GLOBAL_GAIN_MIN, + .flags = 0, + }, { + .ops = &mt9t001_ctrl_ops, + .id = V4L2_CID_GAIN_GREEN_BLUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Green (B)", + .min = MT9T001_GLOBAL_GAIN_MIN, + .max = MT9T001_GLOBAL_GAIN_MAX, + .step = 1, + .def = MT9T001_GLOBAL_GAIN_MIN, + .flags = 0, + }, { + .ops = &mt9t001_ctrl_ops, + .id = V4L2_CID_GAIN_BLUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Blue", + .min = MT9T001_GLOBAL_GAIN_MIN, + .max = MT9T001_GLOBAL_GAIN_MAX, + .step = 1, + .def = MT9T001_GLOBAL_GAIN_MIN, + .flags = 0, + }, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 subdev internal operations + */ + +static int mt9t001_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + crop = v4l2_subdev_get_try_crop(fh, 0); + crop->left = MT9T001_COLUMN_START_DEF; + crop->top = MT9T001_ROW_START_DEF; + crop->width = MT9T001_WINDOW_WIDTH_DEF + 1; + crop->height = MT9T001_WINDOW_HEIGHT_DEF + 1; + + format = v4l2_subdev_get_try_format(fh, 0); + format->code = V4L2_MBUS_FMT_SGRBG10_1X10; + format->width = MT9T001_WINDOW_WIDTH_DEF + 1; + format->height = MT9T001_WINDOW_HEIGHT_DEF + 1; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + return 0; +} + +static struct v4l2_subdev_video_ops mt9t001_subdev_video_ops = { + .s_stream = mt9t001_s_stream, +}; + +static struct v4l2_subdev_pad_ops mt9t001_subdev_pad_ops = { + .enum_mbus_code = mt9t001_enum_mbus_code, + .enum_frame_size = mt9t001_enum_frame_size, + .get_fmt = mt9t001_get_format, + .set_fmt = mt9t001_set_format, + .get_crop = mt9t001_get_crop, + .set_crop = mt9t001_set_crop, +}; + +static struct v4l2_subdev_ops mt9t001_subdev_ops = { + .video = &mt9t001_subdev_video_ops, + .pad = &mt9t001_subdev_pad_ops, +}; + +static struct v4l2_subdev_internal_ops mt9t001_subdev_internal_ops = { + .open = mt9t001_open, +}; + +static int mt9t001_video_probe(struct i2c_client *client) +{ + struct mt9t001_platform_data *pdata = client->dev.platform_data; + s32 data; + int ret; + + dev_info(&client->dev, "Probing MT9T001 at address 0x%02x\n", + client->addr); + + /* Reset the chip and stop data read out */ + ret = mt9t001_write(client, MT9T001_RESET, 1); + if (ret < 0) + return ret; + + ret = mt9t001_write(client, MT9T001_RESET, 0); + if (ret < 0) + return ret; + + ret = mt9t001_write(client, MT9T001_OUTPUT_CONTROL, 0); + if (ret < 0) + return ret; + + /* Configure the pixel clock polarity */ + if (pdata->clk_pol) { + ret = mt9t001_write(client, MT9T001_PIXEL_CLOCK, + MT9T001_PIXEL_CLOCK_INVERT); + if (ret < 0) + return ret; + } + + /* Read and check the sensor version */ + data = mt9t001_read(client, MT9T001_CHIP_VERSION); + if (data != MT9T001_CHIP_ID) { + dev_err(&client->dev, "MT9T001 not detected, wrong version " + "0x%04x\n", data); + return -ENODEV; + } + + dev_info(&client->dev, "MT9T001 detected at address 0x%02x\n", + client->addr); + + return ret; +} + +static int mt9t001_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct mt9t001_platform_data *pdata = client->dev.platform_data; + struct mt9t001 *mt9t001; + unsigned int i; + int ret; + + if (pdata == NULL) { + dev_err(&client->dev, "No platform data\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WORD_DATA)) { + dev_warn(&client->adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + ret = mt9t001_video_probe(client); + if (ret < 0) + return ret; + + mt9t001 = kzalloc(sizeof(*mt9t001), GFP_KERNEL); + if (!mt9t001) + return -ENOMEM; + + v4l2_ctrl_handler_init(&mt9t001->ctrls, ARRAY_SIZE(mt9t001_ctrls) + + ARRAY_SIZE(mt9t001_gains) + 3); + + v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, + V4L2_CID_EXPOSURE, MT9T001_SHUTTER_WIDTH_MIN, + MT9T001_SHUTTER_WIDTH_MAX, 1, + MT9T001_SHUTTER_WIDTH_DEF); + v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, + V4L2_CID_BLACK_LEVEL, 1, 1, 1, 1); + v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, + V4L2_CID_PIXEL_RATE, pdata->ext_clk, pdata->ext_clk, + 1, pdata->ext_clk); + + for (i = 0; i < ARRAY_SIZE(mt9t001_ctrls); ++i) + v4l2_ctrl_new_custom(&mt9t001->ctrls, &mt9t001_ctrls[i], NULL); + + for (i = 0; i < ARRAY_SIZE(mt9t001_gains); ++i) + mt9t001->gains[i] = v4l2_ctrl_new_custom(&mt9t001->ctrls, + &mt9t001_gains[i], NULL); + + v4l2_ctrl_cluster(ARRAY_SIZE(mt9t001_gains), mt9t001->gains); + + mt9t001->subdev.ctrl_handler = &mt9t001->ctrls; + + if (mt9t001->ctrls.error) { + printk(KERN_INFO "%s: control initialization error %d\n", + __func__, mt9t001->ctrls.error); + ret = -EINVAL; + goto done; + } + + mt9t001->crop.left = MT9T001_COLUMN_START_DEF; + mt9t001->crop.top = MT9T001_ROW_START_DEF; + mt9t001->crop.width = MT9T001_WINDOW_WIDTH_DEF + 1; + mt9t001->crop.height = MT9T001_WINDOW_HEIGHT_DEF + 1; + + mt9t001->format.code = V4L2_MBUS_FMT_SGRBG10_1X10; + mt9t001->format.width = MT9T001_WINDOW_WIDTH_DEF + 1; + mt9t001->format.height = MT9T001_WINDOW_HEIGHT_DEF + 1; + mt9t001->format.field = V4L2_FIELD_NONE; + mt9t001->format.colorspace = V4L2_COLORSPACE_SRGB; + + v4l2_i2c_subdev_init(&mt9t001->subdev, client, &mt9t001_subdev_ops); + mt9t001->subdev.internal_ops = &mt9t001_subdev_internal_ops; + mt9t001->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + mt9t001->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&mt9t001->subdev.entity, 1, &mt9t001->pad, 0); + +done: + if (ret < 0) { + v4l2_ctrl_handler_free(&mt9t001->ctrls); + media_entity_cleanup(&mt9t001->subdev.entity); + kfree(mt9t001); + } + + return ret; +} + +static int mt9t001_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct mt9t001 *mt9t001 = to_mt9t001(subdev); + + v4l2_ctrl_handler_free(&mt9t001->ctrls); + v4l2_device_unregister_subdev(subdev); + media_entity_cleanup(&subdev->entity); + kfree(mt9t001); + return 0; +} + +static const struct i2c_device_id mt9t001_id[] = { + { "mt9t001", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mt9t001_id); + +static struct i2c_driver mt9t001_driver = { + .driver = { + .name = "mt9t001", + }, + .probe = mt9t001_probe, + .remove = mt9t001_remove, + .id_table = mt9t001_id, +}; + +module_i2c_driver(mt9t001_driver); + +MODULE_DESCRIPTION("Aptina (Micron) MT9T001 Camera driver"); +MODULE_AUTHOR("Laurent Pinchart "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c new file mode 100644 index 00000000000..6bf01ad6276 --- /dev/null +++ b/drivers/media/i2c/mt9v011.c @@ -0,0 +1,712 @@ +/* + * mt9v011 -Micron 1/4-Inch VGA Digital Image Sensor + * + * Copyright (c) 2009 Mauro Carvalho Chehab (mchehab@redhat.com) + * This code is placed under the terms of the GNU General Public License v2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("Micron mt9v011 sensor driver"); +MODULE_AUTHOR("Mauro Carvalho Chehab "); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-2)"); + +#define R00_MT9V011_CHIP_VERSION 0x00 +#define R01_MT9V011_ROWSTART 0x01 +#define R02_MT9V011_COLSTART 0x02 +#define R03_MT9V011_HEIGHT 0x03 +#define R04_MT9V011_WIDTH 0x04 +#define R05_MT9V011_HBLANK 0x05 +#define R06_MT9V011_VBLANK 0x06 +#define R07_MT9V011_OUT_CTRL 0x07 +#define R09_MT9V011_SHUTTER_WIDTH 0x09 +#define R0A_MT9V011_CLK_SPEED 0x0a +#define R0B_MT9V011_RESTART 0x0b +#define R0C_MT9V011_SHUTTER_DELAY 0x0c +#define R0D_MT9V011_RESET 0x0d +#define R1E_MT9V011_DIGITAL_ZOOM 0x1e +#define R20_MT9V011_READ_MODE 0x20 +#define R2B_MT9V011_GREEN_1_GAIN 0x2b +#define R2C_MT9V011_BLUE_GAIN 0x2c +#define R2D_MT9V011_RED_GAIN 0x2d +#define R2E_MT9V011_GREEN_2_GAIN 0x2e +#define R35_MT9V011_GLOBAL_GAIN 0x35 +#define RF1_MT9V011_CHIP_ENABLE 0xf1 + +#define MT9V011_VERSION 0x8232 +#define MT9V011_REV_B_VERSION 0x8243 + +/* supported controls */ +static struct v4l2_queryctrl mt9v011_qctrl[] = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = (1 << 12) - 1 - 0x0020, + .step = 1, + .default_value = 0x0020, + .flags = 0, + }, { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, + .maximum = 2047, + .step = 1, + .default_value = 0x01fc, + .flags = 0, + }, { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red Balance", + .minimum = -1 << 9, + .maximum = (1 << 9) - 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue Balance", + .minimum = -1 << 9, + .maximum = (1 << 9) - 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vflip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + } +}; + +struct mt9v011 { + struct v4l2_subdev sd; + unsigned width, height; + unsigned xtal; + unsigned hflip:1; + unsigned vflip:1; + + u16 global_gain, exposure; + s16 red_bal, blue_bal; +}; + +static inline struct mt9v011 *to_mt9v011(struct v4l2_subdev *sd) +{ + return container_of(sd, struct mt9v011, sd); +} + +static int mt9v011_read(struct v4l2_subdev *sd, unsigned char addr) +{ + struct i2c_client *c = v4l2_get_subdevdata(sd); + __be16 buffer; + int rc, val; + + rc = i2c_master_send(c, &addr, 1); + if (rc != 1) + v4l2_dbg(0, debug, sd, + "i2c i/o error: rc == %d (should be 1)\n", rc); + + msleep(10); + + rc = i2c_master_recv(c, (char *)&buffer, 2); + if (rc != 2) + v4l2_dbg(0, debug, sd, + "i2c i/o error: rc == %d (should be 2)\n", rc); + + val = be16_to_cpu(buffer); + + v4l2_dbg(2, debug, sd, "mt9v011: read 0x%02x = 0x%04x\n", addr, val); + + return val; +} + +static void mt9v011_write(struct v4l2_subdev *sd, unsigned char addr, + u16 value) +{ + struct i2c_client *c = v4l2_get_subdevdata(sd); + unsigned char buffer[3]; + int rc; + + buffer[0] = addr; + buffer[1] = value >> 8; + buffer[2] = value & 0xff; + + v4l2_dbg(2, debug, sd, + "mt9v011: writing 0x%02x 0x%04x\n", buffer[0], value); + rc = i2c_master_send(c, buffer, 3); + if (rc != 3) + v4l2_dbg(0, debug, sd, + "i2c i/o error: rc == %d (should be 3)\n", rc); +} + + +struct i2c_reg_value { + unsigned char reg; + u16 value; +}; + +/* + * Values used at the original driver + * Some values are marked as Reserved at the datasheet + */ +static const struct i2c_reg_value mt9v011_init_default[] = { + { R0D_MT9V011_RESET, 0x0001 }, + { R0D_MT9V011_RESET, 0x0000 }, + + { R0C_MT9V011_SHUTTER_DELAY, 0x0000 }, + { R09_MT9V011_SHUTTER_WIDTH, 0x1fc }, + + { R0A_MT9V011_CLK_SPEED, 0x0000 }, + { R1E_MT9V011_DIGITAL_ZOOM, 0x0000 }, + + { R07_MT9V011_OUT_CTRL, 0x0002 }, /* chip enable */ +}; + + +static u16 calc_mt9v011_gain(s16 lineargain) +{ + + u16 digitalgain = 0; + u16 analogmult = 0; + u16 analoginit = 0; + + if (lineargain < 0) + lineargain = 0; + + /* recommended minimum */ + lineargain += 0x0020; + + if (lineargain > 2047) + lineargain = 2047; + + if (lineargain > 1023) { + digitalgain = 3; + analogmult = 3; + analoginit = lineargain / 16; + } else if (lineargain > 511) { + digitalgain = 1; + analogmult = 3; + analoginit = lineargain / 8; + } else if (lineargain > 255) { + analogmult = 3; + analoginit = lineargain / 4; + } else if (lineargain > 127) { + analogmult = 1; + analoginit = lineargain / 2; + } else + analoginit = lineargain; + + return analoginit + (analogmult << 7) + (digitalgain << 9); + +} + +static void set_balance(struct v4l2_subdev *sd) +{ + struct mt9v011 *core = to_mt9v011(sd); + u16 green_gain, blue_gain, red_gain; + u16 exposure; + s16 bal; + + exposure = core->exposure; + + green_gain = calc_mt9v011_gain(core->global_gain); + + bal = core->global_gain; + bal += (core->blue_bal * core->global_gain / (1 << 7)); + blue_gain = calc_mt9v011_gain(bal); + + bal = core->global_gain; + bal += (core->red_bal * core->global_gain / (1 << 7)); + red_gain = calc_mt9v011_gain(bal); + + mt9v011_write(sd, R2B_MT9V011_GREEN_1_GAIN, green_gain); + mt9v011_write(sd, R2E_MT9V011_GREEN_2_GAIN, green_gain); + mt9v011_write(sd, R2C_MT9V011_BLUE_GAIN, blue_gain); + mt9v011_write(sd, R2D_MT9V011_RED_GAIN, red_gain); + mt9v011_write(sd, R09_MT9V011_SHUTTER_WIDTH, exposure); +} + +static void calc_fps(struct v4l2_subdev *sd, u32 *numerator, u32 *denominator) +{ + struct mt9v011 *core = to_mt9v011(sd); + unsigned height, width, hblank, vblank, speed; + unsigned row_time, t_time; + u64 frames_per_ms; + unsigned tmp; + + height = mt9v011_read(sd, R03_MT9V011_HEIGHT); + width = mt9v011_read(sd, R04_MT9V011_WIDTH); + hblank = mt9v011_read(sd, R05_MT9V011_HBLANK); + vblank = mt9v011_read(sd, R06_MT9V011_VBLANK); + speed = mt9v011_read(sd, R0A_MT9V011_CLK_SPEED); + + row_time = (width + 113 + hblank) * (speed + 2); + t_time = row_time * (height + vblank + 1); + + frames_per_ms = core->xtal * 1000l; + do_div(frames_per_ms, t_time); + tmp = frames_per_ms; + + v4l2_dbg(1, debug, sd, "Programmed to %u.%03u fps (%d pixel clcks)\n", + tmp / 1000, tmp % 1000, t_time); + + if (numerator && denominator) { + *numerator = 1000; + *denominator = (u32)frames_per_ms; + } +} + +static u16 calc_speed(struct v4l2_subdev *sd, u32 numerator, u32 denominator) +{ + struct mt9v011 *core = to_mt9v011(sd); + unsigned height, width, hblank, vblank; + unsigned row_time, line_time; + u64 t_time, speed; + + /* Avoid bogus calculus */ + if (!numerator || !denominator) + return 0; + + height = mt9v011_read(sd, R03_MT9V011_HEIGHT); + width = mt9v011_read(sd, R04_MT9V011_WIDTH); + hblank = mt9v011_read(sd, R05_MT9V011_HBLANK); + vblank = mt9v011_read(sd, R06_MT9V011_VBLANK); + + row_time = width + 113 + hblank; + line_time = height + vblank + 1; + + t_time = core->xtal * ((u64)numerator); + /* round to the closest value */ + t_time += denominator / 2; + do_div(t_time, denominator); + + speed = t_time; + do_div(speed, row_time * line_time); + + /* Avoid having a negative value for speed */ + if (speed < 2) + speed = 0; + else + speed -= 2; + + /* Avoid speed overflow */ + if (speed > 15) + return 15; + + return (u16)speed; +} + +static void set_res(struct v4l2_subdev *sd) +{ + struct mt9v011 *core = to_mt9v011(sd); + unsigned vstart, hstart; + + /* + * The mt9v011 doesn't have scaling. So, in order to select the desired + * resolution, we're cropping at the middle of the sensor. + * hblank and vblank should be adjusted, in order to warrant that + * we'll preserve the line timings for 30 fps, no matter what resolution + * is selected. + * NOTE: datasheet says that width (and height) should be filled with + * width-1. However, this doesn't work, since one pixel per line will + * be missing. + */ + + hstart = 20 + (640 - core->width) / 2; + mt9v011_write(sd, R02_MT9V011_COLSTART, hstart); + mt9v011_write(sd, R04_MT9V011_WIDTH, core->width); + mt9v011_write(sd, R05_MT9V011_HBLANK, 771 - core->width); + + vstart = 8 + (480 - core->height) / 2; + mt9v011_write(sd, R01_MT9V011_ROWSTART, vstart); + mt9v011_write(sd, R03_MT9V011_HEIGHT, core->height); + mt9v011_write(sd, R06_MT9V011_VBLANK, 508 - core->height); + + calc_fps(sd, NULL, NULL); +}; + +static void set_read_mode(struct v4l2_subdev *sd) +{ + struct mt9v011 *core = to_mt9v011(sd); + unsigned mode = 0x1000; + + if (core->hflip) + mode |= 0x4000; + + if (core->vflip) + mode |= 0x8000; + + mt9v011_write(sd, R20_MT9V011_READ_MODE, mode); +} + +static int mt9v011_reset(struct v4l2_subdev *sd, u32 val) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mt9v011_init_default); i++) + mt9v011_write(sd, mt9v011_init_default[i].reg, + mt9v011_init_default[i].value); + + set_balance(sd); + set_res(sd); + set_read_mode(sd); + + return 0; +}; + +static int mt9v011_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct mt9v011 *core = to_mt9v011(sd); + + v4l2_dbg(1, debug, sd, "g_ctrl called\n"); + + switch (ctrl->id) { + case V4L2_CID_GAIN: + ctrl->value = core->global_gain; + return 0; + case V4L2_CID_EXPOSURE: + ctrl->value = core->exposure; + return 0; + case V4L2_CID_RED_BALANCE: + ctrl->value = core->red_bal; + return 0; + case V4L2_CID_BLUE_BALANCE: + ctrl->value = core->blue_bal; + return 0; + case V4L2_CID_HFLIP: + ctrl->value = core->hflip ? 1 : 0; + return 0; + case V4L2_CID_VFLIP: + ctrl->value = core->vflip ? 1 : 0; + return 0; + } + return -EINVAL; +} + +static int mt9v011_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + int i; + + v4l2_dbg(1, debug, sd, "queryctrl called\n"); + + for (i = 0; i < ARRAY_SIZE(mt9v011_qctrl); i++) + if (qc->id && qc->id == mt9v011_qctrl[i].id) { + memcpy(qc, &(mt9v011_qctrl[i]), + sizeof(*qc)); + return 0; + } + + return -EINVAL; +} + + +static int mt9v011_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct mt9v011 *core = to_mt9v011(sd); + u8 i, n; + n = ARRAY_SIZE(mt9v011_qctrl); + + for (i = 0; i < n; i++) { + if (ctrl->id != mt9v011_qctrl[i].id) + continue; + if (ctrl->value < mt9v011_qctrl[i].minimum || + ctrl->value > mt9v011_qctrl[i].maximum) + return -ERANGE; + v4l2_dbg(1, debug, sd, "s_ctrl: id=%d, value=%d\n", + ctrl->id, ctrl->value); + break; + } + + switch (ctrl->id) { + case V4L2_CID_GAIN: + core->global_gain = ctrl->value; + break; + case V4L2_CID_EXPOSURE: + core->exposure = ctrl->value; + break; + case V4L2_CID_RED_BALANCE: + core->red_bal = ctrl->value; + break; + case V4L2_CID_BLUE_BALANCE: + core->blue_bal = ctrl->value; + break; + case V4L2_CID_HFLIP: + core->hflip = ctrl->value; + set_read_mode(sd); + return 0; + case V4L2_CID_VFLIP: + core->vflip = ctrl->value; + set_read_mode(sd); + return 0; + default: + return -EINVAL; + } + + set_balance(sd); + + return 0; +} + +static int mt9v011_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, + enum v4l2_mbus_pixelcode *code) +{ + if (index > 0) + return -EINVAL; + + *code = V4L2_MBUS_FMT_SGRBG8_1X8; + return 0; +} + +static int mt9v011_try_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) +{ + if (fmt->code != V4L2_MBUS_FMT_SGRBG8_1X8) + return -EINVAL; + + v4l_bound_align_image(&fmt->width, 48, 639, 1, + &fmt->height, 32, 480, 1, 0); + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + + return 0; +} + +static int mt9v011_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memset(cp, 0, sizeof(struct v4l2_captureparm)); + cp->capability = V4L2_CAP_TIMEPERFRAME; + calc_fps(sd, + &cp->timeperframe.numerator, + &cp->timeperframe.denominator); + + return 0; +} + +static int mt9v011_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + struct v4l2_fract *tpf = &cp->timeperframe; + u16 speed; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (cp->extendedmode != 0) + return -EINVAL; + + speed = calc_speed(sd, tpf->numerator, tpf->denominator); + + mt9v011_write(sd, R0A_MT9V011_CLK_SPEED, speed); + v4l2_dbg(1, debug, sd, "Setting speed to %d\n", speed); + + /* Recalculate and update fps info */ + calc_fps(sd, &tpf->numerator, &tpf->denominator); + + return 0; +} + +static int mt9v011_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) +{ + struct mt9v011 *core = to_mt9v011(sd); + int rc; + + rc = mt9v011_try_mbus_fmt(sd, fmt); + if (rc < 0) + return -EINVAL; + + core->width = fmt->width; + core->height = fmt->height; + + set_res(sd); + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int mt9v011_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + reg->val = mt9v011_read(sd, reg->reg & 0xff); + reg->size = 2; + + return 0; +} + +static int mt9v011_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + mt9v011_write(sd, reg->reg & 0xff, reg->val & 0xffff); + + return 0; +} +#endif + +static int mt9v011_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + u16 version; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_MT9V011, + version); +} + +static const struct v4l2_subdev_core_ops mt9v011_core_ops = { + .queryctrl = mt9v011_queryctrl, + .g_ctrl = mt9v011_g_ctrl, + .s_ctrl = mt9v011_s_ctrl, + .reset = mt9v011_reset, + .g_chip_ident = mt9v011_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = mt9v011_g_register, + .s_register = mt9v011_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops mt9v011_video_ops = { + .enum_mbus_fmt = mt9v011_enum_mbus_fmt, + .try_mbus_fmt = mt9v011_try_mbus_fmt, + .s_mbus_fmt = mt9v011_s_mbus_fmt, + .g_parm = mt9v011_g_parm, + .s_parm = mt9v011_s_parm, +}; + +static const struct v4l2_subdev_ops mt9v011_ops = { + .core = &mt9v011_core_ops, + .video = &mt9v011_video_ops, +}; + + +/**************************************************************************** + I2C Client & Driver + ****************************************************************************/ + +static int mt9v011_probe(struct i2c_client *c, + const struct i2c_device_id *id) +{ + u16 version; + struct mt9v011 *core; + struct v4l2_subdev *sd; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(c->adapter, + I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return -EIO; + + core = kzalloc(sizeof(struct mt9v011), GFP_KERNEL); + if (!core) + return -ENOMEM; + + sd = &core->sd; + v4l2_i2c_subdev_init(sd, c, &mt9v011_ops); + + /* Check if the sensor is really a MT9V011 */ + version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION); + if ((version != MT9V011_VERSION) && + (version != MT9V011_REV_B_VERSION)) { + v4l2_info(sd, "*** unknown micron chip detected (0x%04x).\n", + version); + kfree(core); + return -EINVAL; + } + + core->global_gain = 0x0024; + core->exposure = 0x01fc; + core->width = 640; + core->height = 480; + core->xtal = 27000000; /* Hz */ + + if (c->dev.platform_data) { + struct mt9v011_platform_data *pdata = c->dev.platform_data; + + core->xtal = pdata->xtal; + v4l2_dbg(1, debug, sd, "xtal set to %d.%03d MHz\n", + core->xtal / 1000000, (core->xtal / 1000) % 1000); + } + + v4l_info(c, "chip found @ 0x%02x (%s - chip version 0x%04x)\n", + c->addr << 1, c->adapter->name, version); + + return 0; +} + +static int mt9v011_remove(struct i2c_client *c) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(c); + + v4l2_dbg(1, debug, sd, + "mt9v011.c: removing mt9v011 adapter on address 0x%x\n", + c->addr << 1); + + v4l2_device_unregister_subdev(sd); + kfree(to_mt9v011(sd)); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id mt9v011_id[] = { + { "mt9v011", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mt9v011_id); + +static struct i2c_driver mt9v011_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "mt9v011", + }, + .probe = mt9v011_probe, + .remove = mt9v011_remove, + .id_table = mt9v011_id, +}; + +module_i2c_driver(mt9v011_driver); diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c new file mode 100644 index 00000000000..4ba4884c016 --- /dev/null +++ b/drivers/media/i2c/mt9v032.c @@ -0,0 +1,763 @@ +/* + * Driver for MT9V032 CMOS Image Sensor from Micron + * + * Copyright (C) 2010, Laurent Pinchart + * + * Based on the MT9M001 driver, + * + * Copyright (C) 2008, Guennadi Liakhovetski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define MT9V032_PIXEL_ARRAY_HEIGHT 492 +#define MT9V032_PIXEL_ARRAY_WIDTH 782 + +#define MT9V032_CHIP_VERSION 0x00 +#define MT9V032_CHIP_ID_REV1 0x1311 +#define MT9V032_CHIP_ID_REV3 0x1313 +#define MT9V032_COLUMN_START 0x01 +#define MT9V032_COLUMN_START_MIN 1 +#define MT9V032_COLUMN_START_DEF 1 +#define MT9V032_COLUMN_START_MAX 752 +#define MT9V032_ROW_START 0x02 +#define MT9V032_ROW_START_MIN 4 +#define MT9V032_ROW_START_DEF 5 +#define MT9V032_ROW_START_MAX 482 +#define MT9V032_WINDOW_HEIGHT 0x03 +#define MT9V032_WINDOW_HEIGHT_MIN 1 +#define MT9V032_WINDOW_HEIGHT_DEF 480 +#define MT9V032_WINDOW_HEIGHT_MAX 480 +#define MT9V032_WINDOW_WIDTH 0x04 +#define MT9V032_WINDOW_WIDTH_MIN 1 +#define MT9V032_WINDOW_WIDTH_DEF 752 +#define MT9V032_WINDOW_WIDTH_MAX 752 +#define MT9V032_HORIZONTAL_BLANKING 0x05 +#define MT9V032_HORIZONTAL_BLANKING_MIN 43 +#define MT9V032_HORIZONTAL_BLANKING_MAX 1023 +#define MT9V032_VERTICAL_BLANKING 0x06 +#define MT9V032_VERTICAL_BLANKING_MIN 4 +#define MT9V032_VERTICAL_BLANKING_MAX 3000 +#define MT9V032_CHIP_CONTROL 0x07 +#define MT9V032_CHIP_CONTROL_MASTER_MODE (1 << 3) +#define MT9V032_CHIP_CONTROL_DOUT_ENABLE (1 << 7) +#define MT9V032_CHIP_CONTROL_SEQUENTIAL (1 << 8) +#define MT9V032_SHUTTER_WIDTH1 0x08 +#define MT9V032_SHUTTER_WIDTH2 0x09 +#define MT9V032_SHUTTER_WIDTH_CONTROL 0x0a +#define MT9V032_TOTAL_SHUTTER_WIDTH 0x0b +#define MT9V032_TOTAL_SHUTTER_WIDTH_MIN 1 +#define MT9V032_TOTAL_SHUTTER_WIDTH_DEF 480 +#define MT9V032_TOTAL_SHUTTER_WIDTH_MAX 32767 +#define MT9V032_RESET 0x0c +#define MT9V032_READ_MODE 0x0d +#define MT9V032_READ_MODE_ROW_BIN_MASK (3 << 0) +#define MT9V032_READ_MODE_ROW_BIN_SHIFT 0 +#define MT9V032_READ_MODE_COLUMN_BIN_MASK (3 << 2) +#define MT9V032_READ_MODE_COLUMN_BIN_SHIFT 2 +#define MT9V032_READ_MODE_ROW_FLIP (1 << 4) +#define MT9V032_READ_MODE_COLUMN_FLIP (1 << 5) +#define MT9V032_READ_MODE_DARK_COLUMNS (1 << 6) +#define MT9V032_READ_MODE_DARK_ROWS (1 << 7) +#define MT9V032_PIXEL_OPERATION_MODE 0x0f +#define MT9V032_PIXEL_OPERATION_MODE_COLOR (1 << 2) +#define MT9V032_PIXEL_OPERATION_MODE_HDR (1 << 6) +#define MT9V032_ANALOG_GAIN 0x35 +#define MT9V032_ANALOG_GAIN_MIN 16 +#define MT9V032_ANALOG_GAIN_DEF 16 +#define MT9V032_ANALOG_GAIN_MAX 64 +#define MT9V032_MAX_ANALOG_GAIN 0x36 +#define MT9V032_MAX_ANALOG_GAIN_MAX 127 +#define MT9V032_FRAME_DARK_AVERAGE 0x42 +#define MT9V032_DARK_AVG_THRESH 0x46 +#define MT9V032_DARK_AVG_LOW_THRESH_MASK (255 << 0) +#define MT9V032_DARK_AVG_LOW_THRESH_SHIFT 0 +#define MT9V032_DARK_AVG_HIGH_THRESH_MASK (255 << 8) +#define MT9V032_DARK_AVG_HIGH_THRESH_SHIFT 8 +#define MT9V032_ROW_NOISE_CORR_CONTROL 0x70 +#define MT9V032_ROW_NOISE_CORR_ENABLE (1 << 5) +#define MT9V032_ROW_NOISE_CORR_USE_BLK_AVG (1 << 7) +#define MT9V032_PIXEL_CLOCK 0x74 +#define MT9V032_PIXEL_CLOCK_INV_LINE (1 << 0) +#define MT9V032_PIXEL_CLOCK_INV_FRAME (1 << 1) +#define MT9V032_PIXEL_CLOCK_XOR_LINE (1 << 2) +#define MT9V032_PIXEL_CLOCK_CONT_LINE (1 << 3) +#define MT9V032_PIXEL_CLOCK_INV_PXL_CLK (1 << 4) +#define MT9V032_TEST_PATTERN 0x7f +#define MT9V032_TEST_PATTERN_DATA_MASK (1023 << 0) +#define MT9V032_TEST_PATTERN_DATA_SHIFT 0 +#define MT9V032_TEST_PATTERN_USE_DATA (1 << 10) +#define MT9V032_TEST_PATTERN_GRAY_MASK (3 << 11) +#define MT9V032_TEST_PATTERN_GRAY_NONE (0 << 11) +#define MT9V032_TEST_PATTERN_GRAY_VERTICAL (1 << 11) +#define MT9V032_TEST_PATTERN_GRAY_HORIZONTAL (2 << 11) +#define MT9V032_TEST_PATTERN_GRAY_DIAGONAL (3 << 11) +#define MT9V032_TEST_PATTERN_ENABLE (1 << 13) +#define MT9V032_TEST_PATTERN_FLIP (1 << 14) +#define MT9V032_AEC_AGC_ENABLE 0xaf +#define MT9V032_AEC_ENABLE (1 << 0) +#define MT9V032_AGC_ENABLE (1 << 1) +#define MT9V032_THERMAL_INFO 0xc1 + +struct mt9v032 { + struct v4l2_subdev subdev; + struct media_pad pad; + + struct v4l2_mbus_framefmt format; + struct v4l2_rect crop; + + struct v4l2_ctrl_handler ctrls; + + struct mutex power_lock; + int power_count; + + struct mt9v032_platform_data *pdata; + u16 chip_control; + u16 aec_agc; +}; + +static struct mt9v032 *to_mt9v032(struct v4l2_subdev *sd) +{ + return container_of(sd, struct mt9v032, subdev); +} + +static int mt9v032_read(struct i2c_client *client, const u8 reg) +{ + s32 data = i2c_smbus_read_word_swapped(client, reg); + dev_dbg(&client->dev, "%s: read 0x%04x from 0x%02x\n", __func__, + data, reg); + return data; +} + +static int mt9v032_write(struct i2c_client *client, const u8 reg, + const u16 data) +{ + dev_dbg(&client->dev, "%s: writing 0x%04x to 0x%02x\n", __func__, + data, reg); + return i2c_smbus_write_word_swapped(client, reg, data); +} + +static int mt9v032_set_chip_control(struct mt9v032 *mt9v032, u16 clear, u16 set) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); + u16 value = (mt9v032->chip_control & ~clear) | set; + int ret; + + ret = mt9v032_write(client, MT9V032_CHIP_CONTROL, value); + if (ret < 0) + return ret; + + mt9v032->chip_control = value; + return 0; +} + +static int +mt9v032_update_aec_agc(struct mt9v032 *mt9v032, u16 which, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); + u16 value = mt9v032->aec_agc; + int ret; + + if (enable) + value |= which; + else + value &= ~which; + + ret = mt9v032_write(client, MT9V032_AEC_AGC_ENABLE, value); + if (ret < 0) + return ret; + + mt9v032->aec_agc = value; + return 0; +} + +static int mt9v032_power_on(struct mt9v032 *mt9v032) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); + int ret; + + if (mt9v032->pdata->set_clock) { + mt9v032->pdata->set_clock(&mt9v032->subdev, 25000000); + udelay(1); + } + + /* Reset the chip and stop data read out */ + ret = mt9v032_write(client, MT9V032_RESET, 1); + if (ret < 0) + return ret; + + ret = mt9v032_write(client, MT9V032_RESET, 0); + if (ret < 0) + return ret; + + return mt9v032_write(client, MT9V032_CHIP_CONTROL, 0); +} + +static void mt9v032_power_off(struct mt9v032 *mt9v032) +{ + if (mt9v032->pdata->set_clock) + mt9v032->pdata->set_clock(&mt9v032->subdev, 0); +} + +static int __mt9v032_set_power(struct mt9v032 *mt9v032, bool on) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); + int ret; + + if (!on) { + mt9v032_power_off(mt9v032); + return 0; + } + + ret = mt9v032_power_on(mt9v032); + if (ret < 0) + return ret; + + /* Configure the pixel clock polarity */ + if (mt9v032->pdata && mt9v032->pdata->clk_pol) { + ret = mt9v032_write(client, MT9V032_PIXEL_CLOCK, + MT9V032_PIXEL_CLOCK_INV_PXL_CLK); + if (ret < 0) + return ret; + } + + /* Disable the noise correction algorithm and restore the controls. */ + ret = mt9v032_write(client, MT9V032_ROW_NOISE_CORR_CONTROL, 0); + if (ret < 0) + return ret; + + return v4l2_ctrl_handler_setup(&mt9v032->ctrls); +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev video operations + */ + +static struct v4l2_mbus_framefmt * +__mt9v032_get_pad_format(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(fh, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &mt9v032->format; + default: + return NULL; + } +} + +static struct v4l2_rect * +__mt9v032_get_pad_crop(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(fh, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &mt9v032->crop; + default: + return NULL; + } +} + +static int mt9v032_s_stream(struct v4l2_subdev *subdev, int enable) +{ + const u16 mode = MT9V032_CHIP_CONTROL_MASTER_MODE + | MT9V032_CHIP_CONTROL_DOUT_ENABLE + | MT9V032_CHIP_CONTROL_SEQUENTIAL; + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + struct v4l2_mbus_framefmt *format = &mt9v032->format; + struct v4l2_rect *crop = &mt9v032->crop; + unsigned int hratio; + unsigned int vratio; + int ret; + + if (!enable) + return mt9v032_set_chip_control(mt9v032, mode, 0); + + /* Configure the window size and row/column bin */ + hratio = DIV_ROUND_CLOSEST(crop->width, format->width); + vratio = DIV_ROUND_CLOSEST(crop->height, format->height); + + ret = mt9v032_write(client, MT9V032_READ_MODE, + (hratio - 1) << MT9V032_READ_MODE_ROW_BIN_SHIFT | + (vratio - 1) << MT9V032_READ_MODE_COLUMN_BIN_SHIFT); + if (ret < 0) + return ret; + + ret = mt9v032_write(client, MT9V032_COLUMN_START, crop->left); + if (ret < 0) + return ret; + + ret = mt9v032_write(client, MT9V032_ROW_START, crop->top); + if (ret < 0) + return ret; + + ret = mt9v032_write(client, MT9V032_WINDOW_WIDTH, crop->width); + if (ret < 0) + return ret; + + ret = mt9v032_write(client, MT9V032_WINDOW_HEIGHT, crop->height); + if (ret < 0) + return ret; + + ret = mt9v032_write(client, MT9V032_HORIZONTAL_BLANKING, + max(43, 660 - crop->width)); + if (ret < 0) + return ret; + + /* Switch to master "normal" mode */ + return mt9v032_set_chip_control(mt9v032, 0, mode); +} + +static int mt9v032_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = V4L2_MBUS_FMT_SGRBG10_1X10; + return 0; +} + +static int mt9v032_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= 8 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10) + return -EINVAL; + + fse->min_width = MT9V032_WINDOW_WIDTH_DEF / fse->index; + fse->max_width = fse->min_width; + fse->min_height = MT9V032_WINDOW_HEIGHT_DEF / fse->index; + fse->max_height = fse->min_height; + + return 0; +} + +static int mt9v032_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format) +{ + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + + format->format = *__mt9v032_get_pad_format(mt9v032, fh, format->pad, + format->which); + return 0; +} + +static int mt9v032_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format) +{ + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + struct v4l2_mbus_framefmt *__format; + struct v4l2_rect *__crop; + unsigned int width; + unsigned int height; + unsigned int hratio; + unsigned int vratio; + + __crop = __mt9v032_get_pad_crop(mt9v032, fh, format->pad, + format->which); + + /* Clamp the width and height to avoid dividing by zero. */ + width = clamp_t(unsigned int, ALIGN(format->format.width, 2), + max(__crop->width / 8, MT9V032_WINDOW_WIDTH_MIN), + __crop->width); + height = clamp_t(unsigned int, ALIGN(format->format.height, 2), + max(__crop->height / 8, MT9V032_WINDOW_HEIGHT_MIN), + __crop->height); + + hratio = DIV_ROUND_CLOSEST(__crop->width, width); + vratio = DIV_ROUND_CLOSEST(__crop->height, height); + + __format = __mt9v032_get_pad_format(mt9v032, fh, format->pad, + format->which); + __format->width = __crop->width / hratio; + __format->height = __crop->height / vratio; + + format->format = *__format; + + return 0; +} + +static int mt9v032_get_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + + crop->rect = *__mt9v032_get_pad_crop(mt9v032, fh, crop->pad, + crop->which); + return 0; +} + +static int mt9v032_set_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + struct v4l2_mbus_framefmt *__format; + struct v4l2_rect *__crop; + struct v4l2_rect rect; + + /* Clamp the crop rectangle boundaries and align them to a non multiple + * of 2 pixels to ensure a GRBG Bayer pattern. + */ + rect.left = clamp(ALIGN(crop->rect.left + 1, 2) - 1, + MT9V032_COLUMN_START_MIN, + MT9V032_COLUMN_START_MAX); + rect.top = clamp(ALIGN(crop->rect.top + 1, 2) - 1, + MT9V032_ROW_START_MIN, + MT9V032_ROW_START_MAX); + rect.width = clamp(ALIGN(crop->rect.width, 2), + MT9V032_WINDOW_WIDTH_MIN, + MT9V032_WINDOW_WIDTH_MAX); + rect.height = clamp(ALIGN(crop->rect.height, 2), + MT9V032_WINDOW_HEIGHT_MIN, + MT9V032_WINDOW_HEIGHT_MAX); + + rect.width = min(rect.width, MT9V032_PIXEL_ARRAY_WIDTH - rect.left); + rect.height = min(rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top); + + __crop = __mt9v032_get_pad_crop(mt9v032, fh, crop->pad, crop->which); + + if (rect.width != __crop->width || rect.height != __crop->height) { + /* Reset the output image size if the crop rectangle size has + * been modified. + */ + __format = __mt9v032_get_pad_format(mt9v032, fh, crop->pad, + crop->which); + __format->width = rect.width; + __format->height = rect.height; + } + + *__crop = rect; + crop->rect = rect; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev control operations + */ + +#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001) + +static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9v032 *mt9v032 = + container_of(ctrl->handler, struct mt9v032, ctrls); + struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); + u16 data; + + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + return mt9v032_update_aec_agc(mt9v032, MT9V032_AGC_ENABLE, + ctrl->val); + + case V4L2_CID_GAIN: + return mt9v032_write(client, MT9V032_ANALOG_GAIN, ctrl->val); + + case V4L2_CID_EXPOSURE_AUTO: + return mt9v032_update_aec_agc(mt9v032, MT9V032_AEC_ENABLE, + !ctrl->val); + + case V4L2_CID_EXPOSURE: + return mt9v032_write(client, MT9V032_TOTAL_SHUTTER_WIDTH, + ctrl->val); + + case V4L2_CID_TEST_PATTERN: + switch (ctrl->val) { + case 0: + data = 0; + break; + case 1: + data = MT9V032_TEST_PATTERN_GRAY_VERTICAL + | MT9V032_TEST_PATTERN_ENABLE; + break; + case 2: + data = MT9V032_TEST_PATTERN_GRAY_HORIZONTAL + | MT9V032_TEST_PATTERN_ENABLE; + break; + case 3: + data = MT9V032_TEST_PATTERN_GRAY_DIAGONAL + | MT9V032_TEST_PATTERN_ENABLE; + break; + default: + data = (ctrl->val << MT9V032_TEST_PATTERN_DATA_SHIFT) + | MT9V032_TEST_PATTERN_USE_DATA + | MT9V032_TEST_PATTERN_ENABLE + | MT9V032_TEST_PATTERN_FLIP; + break; + } + + return mt9v032_write(client, MT9V032_TEST_PATTERN, data); + } + + return 0; +} + +static struct v4l2_ctrl_ops mt9v032_ctrl_ops = { + .s_ctrl = mt9v032_s_ctrl, +}; + +static const struct v4l2_ctrl_config mt9v032_ctrls[] = { + { + .ops = &mt9v032_ctrl_ops, + .id = V4L2_CID_TEST_PATTERN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Test pattern", + .min = 0, + .max = 1023, + .step = 1, + .def = 0, + .flags = 0, + } +}; + +/* ----------------------------------------------------------------------------- + * V4L2 subdev core operations + */ + +static int mt9v032_set_power(struct v4l2_subdev *subdev, int on) +{ + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + int ret = 0; + + mutex_lock(&mt9v032->power_lock); + + /* If the power count is modified from 0 to != 0 or from != 0 to 0, + * update the power state. + */ + if (mt9v032->power_count == !on) { + ret = __mt9v032_set_power(mt9v032, !!on); + if (ret < 0) + goto done; + } + + /* Update the power count. */ + mt9v032->power_count += on ? 1 : -1; + WARN_ON(mt9v032->power_count < 0); + +done: + mutex_unlock(&mt9v032->power_lock); + return ret; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev internal operations + */ + +static int mt9v032_registered(struct v4l2_subdev *subdev) +{ + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + s32 data; + int ret; + + dev_info(&client->dev, "Probing MT9V032 at address 0x%02x\n", + client->addr); + + ret = mt9v032_power_on(mt9v032); + if (ret < 0) { + dev_err(&client->dev, "MT9V032 power up failed\n"); + return ret; + } + + /* Read and check the sensor version */ + data = mt9v032_read(client, MT9V032_CHIP_VERSION); + if (data != MT9V032_CHIP_ID_REV1 && data != MT9V032_CHIP_ID_REV3) { + dev_err(&client->dev, "MT9V032 not detected, wrong version " + "0x%04x\n", data); + return -ENODEV; + } + + mt9v032_power_off(mt9v032); + + dev_info(&client->dev, "MT9V032 detected at address 0x%02x\n", + client->addr); + + return ret; +} + +static int mt9v032_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + crop = v4l2_subdev_get_try_crop(fh, 0); + crop->left = MT9V032_COLUMN_START_DEF; + crop->top = MT9V032_ROW_START_DEF; + crop->width = MT9V032_WINDOW_WIDTH_DEF; + crop->height = MT9V032_WINDOW_HEIGHT_DEF; + + format = v4l2_subdev_get_try_format(fh, 0); + format->code = V4L2_MBUS_FMT_SGRBG10_1X10; + format->width = MT9V032_WINDOW_WIDTH_DEF; + format->height = MT9V032_WINDOW_HEIGHT_DEF; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + return mt9v032_set_power(subdev, 1); +} + +static int mt9v032_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + return mt9v032_set_power(subdev, 0); +} + +static struct v4l2_subdev_core_ops mt9v032_subdev_core_ops = { + .s_power = mt9v032_set_power, +}; + +static struct v4l2_subdev_video_ops mt9v032_subdev_video_ops = { + .s_stream = mt9v032_s_stream, +}; + +static struct v4l2_subdev_pad_ops mt9v032_subdev_pad_ops = { + .enum_mbus_code = mt9v032_enum_mbus_code, + .enum_frame_size = mt9v032_enum_frame_size, + .get_fmt = mt9v032_get_format, + .set_fmt = mt9v032_set_format, + .get_crop = mt9v032_get_crop, + .set_crop = mt9v032_set_crop, +}; + +static struct v4l2_subdev_ops mt9v032_subdev_ops = { + .core = &mt9v032_subdev_core_ops, + .video = &mt9v032_subdev_video_ops, + .pad = &mt9v032_subdev_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops mt9v032_subdev_internal_ops = { + .registered = mt9v032_registered, + .open = mt9v032_open, + .close = mt9v032_close, +}; + +/* ----------------------------------------------------------------------------- + * Driver initialization and probing + */ + +static int mt9v032_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct mt9v032 *mt9v032; + unsigned int i; + int ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WORD_DATA)) { + dev_warn(&client->adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + mt9v032 = kzalloc(sizeof(*mt9v032), GFP_KERNEL); + if (!mt9v032) + return -ENOMEM; + + mutex_init(&mt9v032->power_lock); + mt9v032->pdata = client->dev.platform_data; + + v4l2_ctrl_handler_init(&mt9v032->ctrls, ARRAY_SIZE(mt9v032_ctrls) + 4); + + v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, + V4L2_CID_GAIN, MT9V032_ANALOG_GAIN_MIN, + MT9V032_ANALOG_GAIN_MAX, 1, MT9V032_ANALOG_GAIN_DEF); + v4l2_ctrl_new_std_menu(&mt9v032->ctrls, &mt9v032_ctrl_ops, + V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0, + V4L2_EXPOSURE_AUTO); + v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, + V4L2_CID_EXPOSURE, MT9V032_TOTAL_SHUTTER_WIDTH_MIN, + MT9V032_TOTAL_SHUTTER_WIDTH_MAX, 1, + MT9V032_TOTAL_SHUTTER_WIDTH_DEF); + + for (i = 0; i < ARRAY_SIZE(mt9v032_ctrls); ++i) + v4l2_ctrl_new_custom(&mt9v032->ctrls, &mt9v032_ctrls[i], NULL); + + mt9v032->subdev.ctrl_handler = &mt9v032->ctrls; + + if (mt9v032->ctrls.error) + printk(KERN_INFO "%s: control initialization error %d\n", + __func__, mt9v032->ctrls.error); + + mt9v032->crop.left = MT9V032_COLUMN_START_DEF; + mt9v032->crop.top = MT9V032_ROW_START_DEF; + mt9v032->crop.width = MT9V032_WINDOW_WIDTH_DEF; + mt9v032->crop.height = MT9V032_WINDOW_HEIGHT_DEF; + + mt9v032->format.code = V4L2_MBUS_FMT_SGRBG10_1X10; + mt9v032->format.width = MT9V032_WINDOW_WIDTH_DEF; + mt9v032->format.height = MT9V032_WINDOW_HEIGHT_DEF; + mt9v032->format.field = V4L2_FIELD_NONE; + mt9v032->format.colorspace = V4L2_COLORSPACE_SRGB; + + mt9v032->aec_agc = MT9V032_AEC_ENABLE | MT9V032_AGC_ENABLE; + + v4l2_i2c_subdev_init(&mt9v032->subdev, client, &mt9v032_subdev_ops); + mt9v032->subdev.internal_ops = &mt9v032_subdev_internal_ops; + mt9v032->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + mt9v032->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&mt9v032->subdev.entity, 1, &mt9v032->pad, 0); + if (ret < 0) + kfree(mt9v032); + + return ret; +} + +static int mt9v032_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + + v4l2_device_unregister_subdev(subdev); + media_entity_cleanup(&subdev->entity); + kfree(mt9v032); + return 0; +} + +static const struct i2c_device_id mt9v032_id[] = { + { "mt9v032", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mt9v032_id); + +static struct i2c_driver mt9v032_driver = { + .driver = { + .name = "mt9v032", + }, + .probe = mt9v032_probe, + .remove = mt9v032_remove, + .id_table = mt9v032_id, +}; + +module_i2c_driver(mt9v032_driver); + +MODULE_DESCRIPTION("Aptina MT9V032 Camera driver"); +MODULE_AUTHOR("Laurent Pinchart "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/noon010pc30.c b/drivers/media/i2c/noon010pc30.c new file mode 100644 index 00000000000..440c12962ba --- /dev/null +++ b/drivers/media/i2c/noon010pc30.c @@ -0,0 +1,851 @@ +/* + * Driver for SiliconFile NOON010PC30 CIF (1/11") Image Sensor with ISP + * + * Copyright (C) 2010 - 2011 Samsung Electronics Co., Ltd. + * Contact: Sylwester Nawrocki, + * + * Initial register configuration based on a driver authored by + * HeungJun Kim . + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Enable module debug trace. Set to 1 to enable."); + +#define MODULE_NAME "NOON010PC30" + +/* + * Register offsets within a page + * b15..b8 - page id, b7..b0 - register address + */ +#define POWER_CTRL_REG 0x0001 +#define PAGEMODE_REG 0x03 +#define DEVICE_ID_REG 0x0004 +#define NOON010PC30_ID 0x86 +#define VDO_CTL_REG(n) (0x0010 + (n)) +#define SYNC_CTL_REG 0x0012 +/* Window size and position */ +#define WIN_ROWH_REG 0x0013 +#define WIN_ROWL_REG 0x0014 +#define WIN_COLH_REG 0x0015 +#define WIN_COLL_REG 0x0016 +#define WIN_HEIGHTH_REG 0x0017 +#define WIN_HEIGHTL_REG 0x0018 +#define WIN_WIDTHH_REG 0x0019 +#define WIN_WIDTHL_REG 0x001A +#define HBLANKH_REG 0x001B +#define HBLANKL_REG 0x001C +#define VSYNCH_REG 0x001D +#define VSYNCL_REG 0x001E +/* VSYNC control */ +#define VS_CTL_REG(n) (0x00A1 + (n)) +/* page 1 */ +#define ISP_CTL_REG(n) (0x0110 + (n)) +#define YOFS_REG 0x0119 +#define DARK_YOFS_REG 0x011A +#define SAT_CTL_REG 0x0120 +#define BSAT_REG 0x0121 +#define RSAT_REG 0x0122 +/* Color correction */ +#define CMC_CTL_REG 0x0130 +#define CMC_OFSGH_REG 0x0133 +#define CMC_OFSGL_REG 0x0135 +#define CMC_SIGN_REG 0x0136 +#define CMC_GOFS_REG 0x0137 +#define CMC_COEF_REG(n) (0x0138 + (n)) +#define CMC_OFS_REG(n) (0x0141 + (n)) +/* Gamma correction */ +#define GMA_CTL_REG 0x0160 +#define GMA_COEF_REG(n) (0x0161 + (n)) +/* Lens Shading */ +#define LENS_CTRL_REG 0x01D0 +#define LENS_XCEN_REG 0x01D1 +#define LENS_YCEN_REG 0x01D2 +#define LENS_RC_REG 0x01D3 +#define LENS_GC_REG 0x01D4 +#define LENS_BC_REG 0x01D5 +#define L_AGON_REG 0x01D6 +#define L_AGOFF_REG 0x01D7 +/* Page 3 - Auto Exposure */ +#define AE_CTL_REG(n) (0x0310 + (n)) +#define AE_CTL9_REG 0x032C +#define AE_CTL10_REG 0x032D +#define AE_YLVL_REG 0x031C +#define AE_YTH_REG(n) (0x031D + (n)) +#define AE_WGT_REG 0x0326 +#define EXP_TIMEH_REG 0x0333 +#define EXP_TIMEM_REG 0x0334 +#define EXP_TIMEL_REG 0x0335 +#define EXP_MMINH_REG 0x0336 +#define EXP_MMINL_REG 0x0337 +#define EXP_MMAXH_REG 0x0338 +#define EXP_MMAXM_REG 0x0339 +#define EXP_MMAXL_REG 0x033A +/* Page 4 - Auto White Balance */ +#define AWB_CTL_REG(n) (0x0410 + (n)) +#define AWB_ENABE 0x80 +#define AWB_WGHT_REG 0x0419 +#define BGAIN_PAR_REG(n) (0x044F + (n)) +/* Manual white balance, when AWB_CTL2[0]=1 */ +#define MWB_RGAIN_REG 0x0466 +#define MWB_BGAIN_REG 0x0467 + +/* The token to mark an array end */ +#define REG_TERM 0xFFFF + +struct noon010_format { + enum v4l2_mbus_pixelcode code; + enum v4l2_colorspace colorspace; + u16 ispctl1_reg; +}; + +struct noon010_frmsize { + u16 width; + u16 height; + int vid_ctl1; +}; + +static const char * const noon010_supply_name[] = { + "vdd_core", "vddio", "vdda" +}; + +#define NOON010_NUM_SUPPLIES ARRAY_SIZE(noon010_supply_name) + +struct noon010_info { + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler hdl; + struct regulator_bulk_data supply[NOON010_NUM_SUPPLIES]; + u32 gpio_nreset; + u32 gpio_nstby; + + /* Protects the struct members below */ + struct mutex lock; + + const struct noon010_format *curr_fmt; + const struct noon010_frmsize *curr_win; + unsigned int apply_new_cfg:1; + unsigned int streaming:1; + unsigned int hflip:1; + unsigned int vflip:1; + unsigned int power:1; + u8 i2c_reg_page; +}; + +struct i2c_regval { + u16 addr; + u16 val; +}; + +/* Supported resolutions. */ +static const struct noon010_frmsize noon010_sizes[] = { + { + .width = 352, + .height = 288, + .vid_ctl1 = 0, + }, { + .width = 176, + .height = 144, + .vid_ctl1 = 0x10, + }, { + .width = 88, + .height = 72, + .vid_ctl1 = 0x20, + }, +}; + +/* Supported pixel formats. */ +static const struct noon010_format noon010_formats[] = { + { + .code = V4L2_MBUS_FMT_YUYV8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .ispctl1_reg = 0x03, + }, { + .code = V4L2_MBUS_FMT_YVYU8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .ispctl1_reg = 0x02, + }, { + .code = V4L2_MBUS_FMT_VYUY8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .ispctl1_reg = 0, + }, { + .code = V4L2_MBUS_FMT_UYVY8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .ispctl1_reg = 0x01, + }, { + .code = V4L2_MBUS_FMT_RGB565_2X8_BE, + .colorspace = V4L2_COLORSPACE_JPEG, + .ispctl1_reg = 0x40, + }, +}; + +static const struct i2c_regval noon010_base_regs[] = { + { WIN_COLL_REG, 0x06 }, { HBLANKL_REG, 0x7C }, + /* Color corection and saturation */ + { ISP_CTL_REG(0), 0x30 }, { ISP_CTL_REG(2), 0x30 }, + { YOFS_REG, 0x80 }, { DARK_YOFS_REG, 0x04 }, + { SAT_CTL_REG, 0x1F }, { BSAT_REG, 0x90 }, + { CMC_CTL_REG, 0x0F }, { CMC_OFSGH_REG, 0x3C }, + { CMC_OFSGL_REG, 0x2C }, { CMC_SIGN_REG, 0x3F }, + { CMC_COEF_REG(0), 0x79 }, { CMC_OFS_REG(0), 0x00 }, + { CMC_COEF_REG(1), 0x39 }, { CMC_OFS_REG(1), 0x00 }, + { CMC_COEF_REG(2), 0x00 }, { CMC_OFS_REG(2), 0x00 }, + { CMC_COEF_REG(3), 0x11 }, { CMC_OFS_REG(3), 0x8B }, + { CMC_COEF_REG(4), 0x65 }, { CMC_OFS_REG(4), 0x07 }, + { CMC_COEF_REG(5), 0x14 }, { CMC_OFS_REG(5), 0x04 }, + { CMC_COEF_REG(6), 0x01 }, { CMC_OFS_REG(6), 0x9C }, + { CMC_COEF_REG(7), 0x33 }, { CMC_OFS_REG(7), 0x89 }, + { CMC_COEF_REG(8), 0x74 }, { CMC_OFS_REG(8), 0x25 }, + /* Automatic white balance */ + { AWB_CTL_REG(0), 0x78 }, { AWB_CTL_REG(1), 0x2E }, + { AWB_CTL_REG(2), 0x20 }, { AWB_CTL_REG(3), 0x85 }, + /* Auto exposure */ + { AE_CTL_REG(0), 0xDC }, { AE_CTL_REG(1), 0x81 }, + { AE_CTL_REG(2), 0x30 }, { AE_CTL_REG(3), 0xA5 }, + { AE_CTL_REG(4), 0x40 }, { AE_CTL_REG(5), 0x51 }, + { AE_CTL_REG(6), 0x33 }, { AE_CTL_REG(7), 0x7E }, + { AE_CTL9_REG, 0x00 }, { AE_CTL10_REG, 0x02 }, + { AE_YLVL_REG, 0x44 }, { AE_YTH_REG(0), 0x34 }, + { AE_YTH_REG(1), 0x30 }, { AE_WGT_REG, 0xD5 }, + /* Lens shading compensation */ + { LENS_CTRL_REG, 0x01 }, { LENS_XCEN_REG, 0x80 }, + { LENS_YCEN_REG, 0x70 }, { LENS_RC_REG, 0x53 }, + { LENS_GC_REG, 0x40 }, { LENS_BC_REG, 0x3E }, + { REG_TERM, 0 }, +}; + +static inline struct noon010_info *to_noon010(struct v4l2_subdev *sd) +{ + return container_of(sd, struct noon010_info, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct noon010_info, hdl)->sd; +} + +static inline int set_i2c_page(struct noon010_info *info, + struct i2c_client *client, unsigned int reg) +{ + u32 page = reg >> 8 & 0xFF; + int ret = 0; + + if (info->i2c_reg_page != page && (reg & 0xFF) != 0x03) { + ret = i2c_smbus_write_byte_data(client, PAGEMODE_REG, page); + if (!ret) + info->i2c_reg_page = page; + } + return ret; +} + +static int cam_i2c_read(struct v4l2_subdev *sd, u32 reg_addr) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct noon010_info *info = to_noon010(sd); + int ret = set_i2c_page(info, client, reg_addr); + + if (ret) + return ret; + return i2c_smbus_read_byte_data(client, reg_addr & 0xFF); +} + +static int cam_i2c_write(struct v4l2_subdev *sd, u32 reg_addr, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct noon010_info *info = to_noon010(sd); + int ret = set_i2c_page(info, client, reg_addr); + + if (ret) + return ret; + return i2c_smbus_write_byte_data(client, reg_addr & 0xFF, val); +} + +static inline int noon010_bulk_write_reg(struct v4l2_subdev *sd, + const struct i2c_regval *msg) +{ + while (msg->addr != REG_TERM) { + int ret = cam_i2c_write(sd, msg->addr, msg->val); + + if (ret) + return ret; + msg++; + } + return 0; +} + +/* Device reset and sleep mode control */ +static int noon010_power_ctrl(struct v4l2_subdev *sd, bool reset, bool sleep) +{ + struct noon010_info *info = to_noon010(sd); + u8 reg = sleep ? 0xF1 : 0xF0; + int ret = 0; + + if (reset) { + ret = cam_i2c_write(sd, POWER_CTRL_REG, reg | 0x02); + udelay(20); + } + if (!ret) { + ret = cam_i2c_write(sd, POWER_CTRL_REG, reg); + if (reset && !ret) + info->i2c_reg_page = -1; + } + return ret; +} + +/* Automatic white balance control */ +static int noon010_enable_autowhitebalance(struct v4l2_subdev *sd, int on) +{ + int ret; + + ret = cam_i2c_write(sd, AWB_CTL_REG(1), on ? 0x2E : 0x2F); + if (!ret) + ret = cam_i2c_write(sd, AWB_CTL_REG(0), on ? 0xFB : 0x7B); + return ret; +} + +/* Called with struct noon010_info.lock mutex held */ +static int noon010_set_flip(struct v4l2_subdev *sd, int hflip, int vflip) +{ + struct noon010_info *info = to_noon010(sd); + int reg, ret; + + reg = cam_i2c_read(sd, VDO_CTL_REG(1)); + if (reg < 0) + return reg; + + reg &= 0x7C; + if (hflip) + reg |= 0x01; + if (vflip) + reg |= 0x02; + + ret = cam_i2c_write(sd, VDO_CTL_REG(1), reg | 0x80); + if (!ret) { + info->hflip = hflip; + info->vflip = vflip; + } + return ret; +} + +/* Configure resolution and color format */ +static int noon010_set_params(struct v4l2_subdev *sd) +{ + struct noon010_info *info = to_noon010(sd); + + int ret = cam_i2c_write(sd, VDO_CTL_REG(0), + info->curr_win->vid_ctl1); + if (ret) + return ret; + return cam_i2c_write(sd, ISP_CTL_REG(0), + info->curr_fmt->ispctl1_reg); +} + +/* Find nearest matching image pixel size. */ +static int noon010_try_frame_size(struct v4l2_mbus_framefmt *mf, + const struct noon010_frmsize **size) +{ + unsigned int min_err = ~0; + int i = ARRAY_SIZE(noon010_sizes); + const struct noon010_frmsize *fsize = &noon010_sizes[0], + *match = NULL; + + while (i--) { + int err = abs(fsize->width - mf->width) + + abs(fsize->height - mf->height); + + if (err < min_err) { + min_err = err; + match = fsize; + } + fsize++; + } + if (match) { + mf->width = match->width; + mf->height = match->height; + if (size) + *size = match; + return 0; + } + return -EINVAL; +} + +/* Called with info.lock mutex held */ +static int power_enable(struct noon010_info *info) +{ + int ret; + + if (info->power) { + v4l2_info(&info->sd, "%s: sensor is already on\n", __func__); + return 0; + } + + if (gpio_is_valid(info->gpio_nstby)) + gpio_set_value(info->gpio_nstby, 0); + + if (gpio_is_valid(info->gpio_nreset)) + gpio_set_value(info->gpio_nreset, 0); + + ret = regulator_bulk_enable(NOON010_NUM_SUPPLIES, info->supply); + if (ret) + return ret; + + if (gpio_is_valid(info->gpio_nreset)) { + msleep(50); + gpio_set_value(info->gpio_nreset, 1); + } + if (gpio_is_valid(info->gpio_nstby)) { + udelay(1000); + gpio_set_value(info->gpio_nstby, 1); + } + if (gpio_is_valid(info->gpio_nreset)) { + udelay(1000); + gpio_set_value(info->gpio_nreset, 0); + msleep(100); + gpio_set_value(info->gpio_nreset, 1); + msleep(20); + } + info->power = 1; + + v4l2_dbg(1, debug, &info->sd, "%s: sensor is on\n", __func__); + return 0; +} + +/* Called with info.lock mutex held */ +static int power_disable(struct noon010_info *info) +{ + int ret; + + if (!info->power) { + v4l2_info(&info->sd, "%s: sensor is already off\n", __func__); + return 0; + } + + ret = regulator_bulk_disable(NOON010_NUM_SUPPLIES, info->supply); + if (ret) + return ret; + + if (gpio_is_valid(info->gpio_nstby)) + gpio_set_value(info->gpio_nstby, 0); + + if (gpio_is_valid(info->gpio_nreset)) + gpio_set_value(info->gpio_nreset, 0); + + info->power = 0; + + v4l2_dbg(1, debug, &info->sd, "%s: sensor is off\n", __func__); + + return 0; +} + +static int noon010_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct noon010_info *info = to_noon010(sd); + int ret = 0; + + v4l2_dbg(1, debug, sd, "%s: ctrl_id: %d, value: %d\n", + __func__, ctrl->id, ctrl->val); + + mutex_lock(&info->lock); + /* + * If the device is not powered up by the host driver do + * not apply any controls to H/W at this time. Instead + * the controls will be restored right after power-up. + */ + if (!info->power) + goto unlock; + + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + ret = noon010_enable_autowhitebalance(sd, ctrl->val); + break; + case V4L2_CID_BLUE_BALANCE: + ret = cam_i2c_write(sd, MWB_BGAIN_REG, ctrl->val); + break; + case V4L2_CID_RED_BALANCE: + ret = cam_i2c_write(sd, MWB_RGAIN_REG, ctrl->val); + break; + default: + ret = -EINVAL; + } +unlock: + mutex_unlock(&info->lock); + return ret; +} + +static int noon010_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(noon010_formats)) + return -EINVAL; + + code->code = noon010_formats[code->index].code; + return 0; +} + +static int noon010_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct noon010_info *info = to_noon010(sd); + struct v4l2_mbus_framefmt *mf; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + if (fh) { + mf = v4l2_subdev_get_try_format(fh, 0); + fmt->format = *mf; + } + return 0; + } + mf = &fmt->format; + + mutex_lock(&info->lock); + mf->width = info->curr_win->width; + mf->height = info->curr_win->height; + mf->code = info->curr_fmt->code; + mf->colorspace = info->curr_fmt->colorspace; + mf->field = V4L2_FIELD_NONE; + + mutex_unlock(&info->lock); + return 0; +} + +/* Return nearest media bus frame format. */ +static const struct noon010_format *noon010_try_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + int i = ARRAY_SIZE(noon010_formats); + + while (--i) + if (mf->code == noon010_formats[i].code) + break; + mf->code = noon010_formats[i].code; + + return &noon010_formats[i]; +} + +static int noon010_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct noon010_info *info = to_noon010(sd); + const struct noon010_frmsize *size = NULL; + const struct noon010_format *nf; + struct v4l2_mbus_framefmt *mf; + int ret = 0; + + nf = noon010_try_fmt(sd, &fmt->format); + noon010_try_frame_size(&fmt->format, &size); + fmt->format.colorspace = V4L2_COLORSPACE_JPEG; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + if (fh) { + mf = v4l2_subdev_get_try_format(fh, 0); + *mf = fmt->format; + } + return 0; + } + mutex_lock(&info->lock); + if (!info->streaming) { + info->apply_new_cfg = 1; + info->curr_fmt = nf; + info->curr_win = size; + } else { + ret = -EBUSY; + } + mutex_unlock(&info->lock); + return ret; +} + +/* Called with struct noon010_info.lock mutex held */ +static int noon010_base_config(struct v4l2_subdev *sd) +{ + int ret = noon010_bulk_write_reg(sd, noon010_base_regs); + if (!ret) + ret = noon010_set_params(sd); + if (!ret) + ret = noon010_set_flip(sd, 1, 0); + + return ret; +} + +static int noon010_s_power(struct v4l2_subdev *sd, int on) +{ + struct noon010_info *info = to_noon010(sd); + int ret; + + mutex_lock(&info->lock); + if (on) { + ret = power_enable(info); + if (!ret) + ret = noon010_base_config(sd); + } else { + noon010_power_ctrl(sd, false, true); + ret = power_disable(info); + } + mutex_unlock(&info->lock); + + /* Restore the controls state */ + if (!ret && on) + ret = v4l2_ctrl_handler_setup(&info->hdl); + + return ret; +} + +static int noon010_s_stream(struct v4l2_subdev *sd, int on) +{ + struct noon010_info *info = to_noon010(sd); + int ret = 0; + + mutex_lock(&info->lock); + if (!info->streaming != !on) { + ret = noon010_power_ctrl(sd, false, !on); + if (!ret) + info->streaming = on; + } + if (!ret && on && info->apply_new_cfg) { + ret = noon010_set_params(sd); + if (!ret) + info->apply_new_cfg = 0; + } + mutex_unlock(&info->lock); + return ret; +} + +static int noon010_log_status(struct v4l2_subdev *sd) +{ + struct noon010_info *info = to_noon010(sd); + + v4l2_ctrl_handler_log_status(&info->hdl, sd->name); + return 0; +} + +static int noon010_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(fh, 0); + + mf->width = noon010_sizes[0].width; + mf->height = noon010_sizes[0].height; + mf->code = noon010_formats[0].code; + mf->colorspace = V4L2_COLORSPACE_JPEG; + mf->field = V4L2_FIELD_NONE; + return 0; +} + +static const struct v4l2_subdev_internal_ops noon010_subdev_internal_ops = { + .open = noon010_open, +}; + +static const struct v4l2_ctrl_ops noon010_ctrl_ops = { + .s_ctrl = noon010_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops noon010_core_ops = { + .s_power = noon010_s_power, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .log_status = noon010_log_status, +}; + +static struct v4l2_subdev_pad_ops noon010_pad_ops = { + .enum_mbus_code = noon010_enum_mbus_code, + .get_fmt = noon010_get_fmt, + .set_fmt = noon010_set_fmt, +}; + +static struct v4l2_subdev_video_ops noon010_video_ops = { + .s_stream = noon010_s_stream, +}; + +static const struct v4l2_subdev_ops noon010_ops = { + .core = &noon010_core_ops, + .pad = &noon010_pad_ops, + .video = &noon010_video_ops, +}; + +/* Return 0 if NOON010PC30L sensor type was detected or -ENODEV otherwise. */ +static int noon010_detect(struct i2c_client *client, struct noon010_info *info) +{ + int ret; + + ret = power_enable(info); + if (ret) + return ret; + + ret = i2c_smbus_read_byte_data(client, DEVICE_ID_REG); + if (ret < 0) + dev_err(&client->dev, "I2C read failed: 0x%X\n", ret); + + power_disable(info); + + return ret == NOON010PC30_ID ? 0 : -ENODEV; +} + +static int noon010_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct noon010_info *info; + struct v4l2_subdev *sd; + const struct noon010pc30_platform_data *pdata + = client->dev.platform_data; + int ret; + int i; + + if (!pdata) { + dev_err(&client->dev, "No platform data!\n"); + return -EIO; + } + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + mutex_init(&info->lock); + sd = &info->sd; + v4l2_i2c_subdev_init(sd, client, &noon010_ops); + strlcpy(sd->name, MODULE_NAME, sizeof(sd->name)); + + sd->internal_ops = &noon010_subdev_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + v4l2_ctrl_handler_init(&info->hdl, 3); + + v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, + V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); + v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, + V4L2_CID_RED_BALANCE, 0, 127, 1, 64); + v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, + V4L2_CID_BLUE_BALANCE, 0, 127, 1, 64); + + sd->ctrl_handler = &info->hdl; + + ret = info->hdl.error; + if (ret) + goto np_err; + + info->i2c_reg_page = -1; + info->gpio_nreset = -EINVAL; + info->gpio_nstby = -EINVAL; + info->curr_fmt = &noon010_formats[0]; + info->curr_win = &noon010_sizes[0]; + + if (gpio_is_valid(pdata->gpio_nreset)) { + ret = gpio_request(pdata->gpio_nreset, "NOON010PC30 NRST"); + if (ret) { + dev_err(&client->dev, "GPIO request error: %d\n", ret); + goto np_err; + } + info->gpio_nreset = pdata->gpio_nreset; + gpio_direction_output(info->gpio_nreset, 0); + gpio_export(info->gpio_nreset, 0); + } + + if (gpio_is_valid(pdata->gpio_nstby)) { + ret = gpio_request(pdata->gpio_nstby, "NOON010PC30 NSTBY"); + if (ret) { + dev_err(&client->dev, "GPIO request error: %d\n", ret); + goto np_gpio_err; + } + info->gpio_nstby = pdata->gpio_nstby; + gpio_direction_output(info->gpio_nstby, 0); + gpio_export(info->gpio_nstby, 0); + } + + for (i = 0; i < NOON010_NUM_SUPPLIES; i++) + info->supply[i].supply = noon010_supply_name[i]; + + ret = regulator_bulk_get(&client->dev, NOON010_NUM_SUPPLIES, + info->supply); + if (ret) + goto np_reg_err; + + info->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; + ret = media_entity_init(&sd->entity, 1, &info->pad, 0); + if (ret < 0) + goto np_me_err; + + ret = noon010_detect(client, info); + if (!ret) + return 0; + +np_me_err: + regulator_bulk_free(NOON010_NUM_SUPPLIES, info->supply); +np_reg_err: + if (gpio_is_valid(info->gpio_nstby)) + gpio_free(info->gpio_nstby); +np_gpio_err: + if (gpio_is_valid(info->gpio_nreset)) + gpio_free(info->gpio_nreset); +np_err: + v4l2_ctrl_handler_free(&info->hdl); + v4l2_device_unregister_subdev(sd); + kfree(info); + return ret; +} + +static int noon010_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct noon010_info *info = to_noon010(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&info->hdl); + + regulator_bulk_free(NOON010_NUM_SUPPLIES, info->supply); + + if (gpio_is_valid(info->gpio_nreset)) + gpio_free(info->gpio_nreset); + + if (gpio_is_valid(info->gpio_nstby)) + gpio_free(info->gpio_nstby); + + media_entity_cleanup(&sd->entity); + kfree(info); + return 0; +} + +static const struct i2c_device_id noon010_id[] = { + { MODULE_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, noon010_id); + + +static struct i2c_driver noon010_i2c_driver = { + .driver = { + .name = MODULE_NAME + }, + .probe = noon010_probe, + .remove = noon010_remove, + .id_table = noon010_id, +}; + +module_i2c_driver(noon010_i2c_driver); + +MODULE_DESCRIPTION("Siliconfile NOON010PC30 camera driver"); +MODULE_AUTHOR("Sylwester Nawrocki "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c new file mode 100644 index 00000000000..e7c82b29751 --- /dev/null +++ b/drivers/media/i2c/ov7670.c @@ -0,0 +1,1586 @@ +/* + * A V4L2 driver for OmniVision OV7670 cameras. + * + * Copyright 2006 One Laptop Per Child Association, Inc. Written + * by Jonathan Corbet with substantial inspiration from Mark + * McClelland's ovcamchip code. + * + * Copyright 2006-7 Jonathan Corbet + * + * This file may be distributed under the terms of the GNU General + * Public License, version 2. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jonathan Corbet "); +MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors"); +MODULE_LICENSE("GPL"); + +static bool debug; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +/* + * Basic window sizes. These probably belong somewhere more globally + * useful. + */ +#define VGA_WIDTH 640 +#define VGA_HEIGHT 480 +#define QVGA_WIDTH 320 +#define QVGA_HEIGHT 240 +#define CIF_WIDTH 352 +#define CIF_HEIGHT 288 +#define QCIF_WIDTH 176 +#define QCIF_HEIGHT 144 + +/* + * The 7670 sits on i2c with ID 0x42 + */ +#define OV7670_I2C_ADDR 0x42 + +/* Registers */ +#define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ +#define REG_BLUE 0x01 /* blue gain */ +#define REG_RED 0x02 /* red gain */ +#define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ +#define REG_COM1 0x04 /* Control 1 */ +#define COM1_CCIR656 0x40 /* CCIR656 enable */ +#define REG_BAVE 0x05 /* U/B Average level */ +#define REG_GbAVE 0x06 /* Y/Gb Average level */ +#define REG_AECHH 0x07 /* AEC MS 5 bits */ +#define REG_RAVE 0x08 /* V/R Average level */ +#define REG_COM2 0x09 /* Control 2 */ +#define COM2_SSLEEP 0x10 /* Soft sleep mode */ +#define REG_PID 0x0a /* Product ID MSB */ +#define REG_VER 0x0b /* Product ID LSB */ +#define REG_COM3 0x0c /* Control 3 */ +#define COM3_SWAP 0x40 /* Byte swap */ +#define COM3_SCALEEN 0x08 /* Enable scaling */ +#define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */ +#define REG_COM4 0x0d /* Control 4 */ +#define REG_COM5 0x0e /* All "reserved" */ +#define REG_COM6 0x0f /* Control 6 */ +#define REG_AECH 0x10 /* More bits of AEC value */ +#define REG_CLKRC 0x11 /* Clocl control */ +#define CLK_EXT 0x40 /* Use external clock directly */ +#define CLK_SCALE 0x3f /* Mask for internal clock scale */ +#define REG_COM7 0x12 /* Control 7 */ +#define COM7_RESET 0x80 /* Register reset */ +#define COM7_FMT_MASK 0x38 +#define COM7_FMT_VGA 0x00 +#define COM7_FMT_CIF 0x20 /* CIF format */ +#define COM7_FMT_QVGA 0x10 /* QVGA format */ +#define COM7_FMT_QCIF 0x08 /* QCIF format */ +#define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */ +#define COM7_YUV 0x00 /* YUV */ +#define COM7_BAYER 0x01 /* Bayer format */ +#define COM7_PBAYER 0x05 /* "Processed bayer" */ +#define REG_COM8 0x13 /* Control 8 */ +#define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ +#define COM8_AECSTEP 0x40 /* Unlimited AEC step size */ +#define COM8_BFILT 0x20 /* Band filter enable */ +#define COM8_AGC 0x04 /* Auto gain enable */ +#define COM8_AWB 0x02 /* White balance enable */ +#define COM8_AEC 0x01 /* Auto exposure enable */ +#define REG_COM9 0x14 /* Control 9 - gain ceiling */ +#define REG_COM10 0x15 /* Control 10 */ +#define COM10_HSYNC 0x40 /* HSYNC instead of HREF */ +#define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */ +#define COM10_HREF_REV 0x08 /* Reverse HREF */ +#define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */ +#define COM10_VS_NEG 0x02 /* VSYNC negative */ +#define COM10_HS_NEG 0x01 /* HSYNC negative */ +#define REG_HSTART 0x17 /* Horiz start high bits */ +#define REG_HSTOP 0x18 /* Horiz stop high bits */ +#define REG_VSTART 0x19 /* Vert start high bits */ +#define REG_VSTOP 0x1a /* Vert stop high bits */ +#define REG_PSHFT 0x1b /* Pixel delay after HREF */ +#define REG_MIDH 0x1c /* Manuf. ID high */ +#define REG_MIDL 0x1d /* Manuf. ID low */ +#define REG_MVFP 0x1e /* Mirror / vflip */ +#define MVFP_MIRROR 0x20 /* Mirror image */ +#define MVFP_FLIP 0x10 /* Vertical flip */ + +#define REG_AEW 0x24 /* AGC upper limit */ +#define REG_AEB 0x25 /* AGC lower limit */ +#define REG_VPT 0x26 /* AGC/AEC fast mode op region */ +#define REG_HSYST 0x30 /* HSYNC rising edge delay */ +#define REG_HSYEN 0x31 /* HSYNC falling edge delay */ +#define REG_HREF 0x32 /* HREF pieces */ +#define REG_TSLB 0x3a /* lots of stuff */ +#define TSLB_YLAST 0x04 /* UYVY or VYUY - see com13 */ +#define REG_COM11 0x3b /* Control 11 */ +#define COM11_NIGHT 0x80 /* NIght mode enable */ +#define COM11_NMFR 0x60 /* Two bit NM frame rate */ +#define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ +#define COM11_50HZ 0x08 /* Manual 50Hz select */ +#define COM11_EXP 0x02 +#define REG_COM12 0x3c /* Control 12 */ +#define COM12_HREF 0x80 /* HREF always */ +#define REG_COM13 0x3d /* Control 13 */ +#define COM13_GAMMA 0x80 /* Gamma enable */ +#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ +#define COM13_UVSWAP 0x01 /* V before U - w/TSLB */ +#define REG_COM14 0x3e /* Control 14 */ +#define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */ +#define REG_EDGE 0x3f /* Edge enhancement factor */ +#define REG_COM15 0x40 /* Control 15 */ +#define COM15_R10F0 0x00 /* Data range 10 to F0 */ +#define COM15_R01FE 0x80 /* 01 to FE */ +#define COM15_R00FF 0xc0 /* 00 to FF */ +#define COM15_RGB565 0x10 /* RGB565 output */ +#define COM15_RGB555 0x30 /* RGB555 output */ +#define REG_COM16 0x41 /* Control 16 */ +#define COM16_AWBGAIN 0x08 /* AWB gain enable */ +#define REG_COM17 0x42 /* Control 17 */ +#define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */ +#define COM17_CBAR 0x08 /* DSP Color bar */ + +/* + * This matrix defines how the colors are generated, must be + * tweaked to adjust hue and saturation. + * + * Order: v-red, v-green, v-blue, u-red, u-green, u-blue + * + * They are nine-bit signed quantities, with the sign bit + * stored in 0x58. Sign for v-red is bit 0, and up from there. + */ +#define REG_CMATRIX_BASE 0x4f +#define CMATRIX_LEN 6 +#define REG_CMATRIX_SIGN 0x58 + + +#define REG_BRIGHT 0x55 /* Brightness */ +#define REG_CONTRAS 0x56 /* Contrast control */ + +#define REG_GFIX 0x69 /* Fix gain control */ + +#define REG_REG76 0x76 /* OV's name */ +#define R76_BLKPCOR 0x80 /* Black pixel correction enable */ +#define R76_WHTPCOR 0x40 /* White pixel correction enable */ + +#define REG_RGB444 0x8c /* RGB 444 control */ +#define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */ +#define R444_RGBX 0x01 /* Empty nibble at end */ + +#define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */ +#define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ + +#define REG_BD50MAX 0xa5 /* 50hz banding step limit */ +#define REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */ +#define REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */ +#define REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */ +#define REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */ +#define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ +#define REG_BD60MAX 0xab /* 60hz banding step limit */ + + +/* + * Information we maintain about a known sensor. + */ +struct ov7670_format_struct; /* coming later */ +struct ov7670_info { + struct v4l2_subdev sd; + struct ov7670_format_struct *fmt; /* Current format */ + unsigned char sat; /* Saturation value */ + int hue; /* Hue value */ + int min_width; /* Filter out smaller sizes */ + int min_height; /* Filter out smaller sizes */ + int clock_speed; /* External clock speed (MHz) */ + u8 clkrc; /* Clock divider value */ + bool use_smbus; /* Use smbus I/O instead of I2C */ +}; + +static inline struct ov7670_info *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ov7670_info, sd); +} + + + +/* + * The default register settings, as obtained from OmniVision. There + * is really no making sense of most of these - lots of "reserved" values + * and such. + * + * These settings give VGA YUYV. + */ + +struct regval_list { + unsigned char reg_num; + unsigned char value; +}; + +static struct regval_list ov7670_default_regs[] = { + { REG_COM7, COM7_RESET }, +/* + * Clock scale: 3 = 15fps + * 2 = 20fps + * 1 = 30fps + */ + { REG_CLKRC, 0x1 }, /* OV: clock scale (30 fps) */ + { REG_TSLB, 0x04 }, /* OV */ + { REG_COM7, 0 }, /* VGA */ + /* + * Set the hardware window. These values from OV don't entirely + * make sense - hstop is less than hstart. But they work... + */ + { REG_HSTART, 0x13 }, { REG_HSTOP, 0x01 }, + { REG_HREF, 0xb6 }, { REG_VSTART, 0x02 }, + { REG_VSTOP, 0x7a }, { REG_VREF, 0x0a }, + + { REG_COM3, 0 }, { REG_COM14, 0 }, + /* Mystery scaling numbers */ + { 0x70, 0x3a }, { 0x71, 0x35 }, + { 0x72, 0x11 }, { 0x73, 0xf0 }, + { 0xa2, 0x02 }, { REG_COM10, 0x0 }, + + /* Gamma curve values */ + { 0x7a, 0x20 }, { 0x7b, 0x10 }, + { 0x7c, 0x1e }, { 0x7d, 0x35 }, + { 0x7e, 0x5a }, { 0x7f, 0x69 }, + { 0x80, 0x76 }, { 0x81, 0x80 }, + { 0x82, 0x88 }, { 0x83, 0x8f }, + { 0x84, 0x96 }, { 0x85, 0xa3 }, + { 0x86, 0xaf }, { 0x87, 0xc4 }, + { 0x88, 0xd7 }, { 0x89, 0xe8 }, + + /* AGC and AEC parameters. Note we start by disabling those features, + then turn them only after tweaking the values. */ + { REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT }, + { REG_GAIN, 0 }, { REG_AECH, 0 }, + { REG_COM4, 0x40 }, /* magic reserved bit */ + { REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */ + { REG_BD50MAX, 0x05 }, { REG_BD60MAX, 0x07 }, + { REG_AEW, 0x95 }, { REG_AEB, 0x33 }, + { REG_VPT, 0xe3 }, { REG_HAECC1, 0x78 }, + { REG_HAECC2, 0x68 }, { 0xa1, 0x03 }, /* magic */ + { REG_HAECC3, 0xd8 }, { REG_HAECC4, 0xd8 }, + { REG_HAECC5, 0xf0 }, { REG_HAECC6, 0x90 }, + { REG_HAECC7, 0x94 }, + { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC }, + + /* Almost all of these are magic "reserved" values. */ + { REG_COM5, 0x61 }, { REG_COM6, 0x4b }, + { 0x16, 0x02 }, { REG_MVFP, 0x07 }, + { 0x21, 0x02 }, { 0x22, 0x91 }, + { 0x29, 0x07 }, { 0x33, 0x0b }, + { 0x35, 0x0b }, { 0x37, 0x1d }, + { 0x38, 0x71 }, { 0x39, 0x2a }, + { REG_COM12, 0x78 }, { 0x4d, 0x40 }, + { 0x4e, 0x20 }, { REG_GFIX, 0 }, + { 0x6b, 0x4a }, { 0x74, 0x10 }, + { 0x8d, 0x4f }, { 0x8e, 0 }, + { 0x8f, 0 }, { 0x90, 0 }, + { 0x91, 0 }, { 0x96, 0 }, + { 0x9a, 0 }, { 0xb0, 0x84 }, + { 0xb1, 0x0c }, { 0xb2, 0x0e }, + { 0xb3, 0x82 }, { 0xb8, 0x0a }, + + /* More reserved magic, some of which tweaks white balance */ + { 0x43, 0x0a }, { 0x44, 0xf0 }, + { 0x45, 0x34 }, { 0x46, 0x58 }, + { 0x47, 0x28 }, { 0x48, 0x3a }, + { 0x59, 0x88 }, { 0x5a, 0x88 }, + { 0x5b, 0x44 }, { 0x5c, 0x67 }, + { 0x5d, 0x49 }, { 0x5e, 0x0e }, + { 0x6c, 0x0a }, { 0x6d, 0x55 }, + { 0x6e, 0x11 }, { 0x6f, 0x9f }, /* "9e for advance AWB" */ + { 0x6a, 0x40 }, { REG_BLUE, 0x40 }, + { REG_RED, 0x60 }, + { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC|COM8_AWB }, + + /* Matrix coefficients */ + { 0x4f, 0x80 }, { 0x50, 0x80 }, + { 0x51, 0 }, { 0x52, 0x22 }, + { 0x53, 0x5e }, { 0x54, 0x80 }, + { 0x58, 0x9e }, + + { REG_COM16, COM16_AWBGAIN }, { REG_EDGE, 0 }, + { 0x75, 0x05 }, { 0x76, 0xe1 }, + { 0x4c, 0 }, { 0x77, 0x01 }, + { REG_COM13, 0xc3 }, { 0x4b, 0x09 }, + { 0xc9, 0x60 }, { REG_COM16, 0x38 }, + { 0x56, 0x40 }, + + { 0x34, 0x11 }, { REG_COM11, COM11_EXP|COM11_HZAUTO }, + { 0xa4, 0x88 }, { 0x96, 0 }, + { 0x97, 0x30 }, { 0x98, 0x20 }, + { 0x99, 0x30 }, { 0x9a, 0x84 }, + { 0x9b, 0x29 }, { 0x9c, 0x03 }, + { 0x9d, 0x4c }, { 0x9e, 0x3f }, + { 0x78, 0x04 }, + + /* Extra-weird stuff. Some sort of multiplexor register */ + { 0x79, 0x01 }, { 0xc8, 0xf0 }, + { 0x79, 0x0f }, { 0xc8, 0x00 }, + { 0x79, 0x10 }, { 0xc8, 0x7e }, + { 0x79, 0x0a }, { 0xc8, 0x80 }, + { 0x79, 0x0b }, { 0xc8, 0x01 }, + { 0x79, 0x0c }, { 0xc8, 0x0f }, + { 0x79, 0x0d }, { 0xc8, 0x20 }, + { 0x79, 0x09 }, { 0xc8, 0x80 }, + { 0x79, 0x02 }, { 0xc8, 0xc0 }, + { 0x79, 0x03 }, { 0xc8, 0x40 }, + { 0x79, 0x05 }, { 0xc8, 0x30 }, + { 0x79, 0x26 }, + + { 0xff, 0xff }, /* END MARKER */ +}; + + +/* + * Here we'll try to encapsulate the changes for just the output + * video format. + * + * RGB656 and YUV422 come from OV; RGB444 is homebrewed. + * + * IMPORTANT RULE: the first entry must be for COM7, see ov7670_s_fmt for why. + */ + + +static struct regval_list ov7670_fmt_yuv422[] = { + { REG_COM7, 0x0 }, /* Selects YUV mode */ + { REG_RGB444, 0 }, /* No RGB444 please */ + { REG_COM1, 0 }, /* CCIR601 */ + { REG_COM15, COM15_R00FF }, + { REG_COM9, 0x18 }, /* 4x gain ceiling; 0x8 is reserved bit */ + { 0x4f, 0x80 }, /* "matrix coefficient 1" */ + { 0x50, 0x80 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x22 }, /* "matrix coefficient 4" */ + { 0x53, 0x5e }, /* "matrix coefficient 5" */ + { 0x54, 0x80 }, /* "matrix coefficient 6" */ + { REG_COM13, COM13_GAMMA|COM13_UVSAT }, + { 0xff, 0xff }, +}; + +static struct regval_list ov7670_fmt_rgb565[] = { + { REG_COM7, COM7_RGB }, /* Selects RGB mode */ + { REG_RGB444, 0 }, /* No RGB444 please */ + { REG_COM1, 0x0 }, /* CCIR601 */ + { REG_COM15, COM15_RGB565 }, + { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ + { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ + { 0x50, 0xb3 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x3d }, /* "matrix coefficient 4" */ + { 0x53, 0xa7 }, /* "matrix coefficient 5" */ + { 0x54, 0xe4 }, /* "matrix coefficient 6" */ + { REG_COM13, COM13_GAMMA|COM13_UVSAT }, + { 0xff, 0xff }, +}; + +static struct regval_list ov7670_fmt_rgb444[] = { + { REG_COM7, COM7_RGB }, /* Selects RGB mode */ + { REG_RGB444, R444_ENABLE }, /* Enable xxxxrrrr ggggbbbb */ + { REG_COM1, 0x0 }, /* CCIR601 */ + { REG_COM15, COM15_R01FE|COM15_RGB565 }, /* Data range needed? */ + { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ + { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ + { 0x50, 0xb3 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x3d }, /* "matrix coefficient 4" */ + { 0x53, 0xa7 }, /* "matrix coefficient 5" */ + { 0x54, 0xe4 }, /* "matrix coefficient 6" */ + { REG_COM13, COM13_GAMMA|COM13_UVSAT|0x2 }, /* Magic rsvd bit */ + { 0xff, 0xff }, +}; + +static struct regval_list ov7670_fmt_raw[] = { + { REG_COM7, COM7_BAYER }, + { REG_COM13, 0x08 }, /* No gamma, magic rsvd bit */ + { REG_COM16, 0x3d }, /* Edge enhancement, denoise */ + { REG_REG76, 0xe1 }, /* Pix correction, magic rsvd */ + { 0xff, 0xff }, +}; + + + +/* + * Low-level register I/O. + * + * Note that there are two versions of these. On the XO 1, the + * i2c controller only does SMBUS, so that's what we use. The + * ov7670 is not really an SMBUS device, though, so the communication + * is not always entirely reliable. + */ +static int ov7670_read_smbus(struct v4l2_subdev *sd, unsigned char reg, + unsigned char *value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + + ret = i2c_smbus_read_byte_data(client, reg); + if (ret >= 0) { + *value = (unsigned char)ret; + ret = 0; + } + return ret; +} + + +static int ov7670_write_smbus(struct v4l2_subdev *sd, unsigned char reg, + unsigned char value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = i2c_smbus_write_byte_data(client, reg, value); + + if (reg == REG_COM7 && (value & COM7_RESET)) + msleep(5); /* Wait for reset to run */ + return ret; +} + +/* + * On most platforms, we'd rather do straight i2c I/O. + */ +static int ov7670_read_i2c(struct v4l2_subdev *sd, unsigned char reg, + unsigned char *value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 data = reg; + struct i2c_msg msg; + int ret; + + /* + * Send out the register address... + */ + msg.addr = client->addr; + msg.flags = 0; + msg.len = 1; + msg.buf = &data; + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) { + printk(KERN_ERR "Error %d on register write\n", ret); + return ret; + } + /* + * ...then read back the result. + */ + msg.flags = I2C_M_RD; + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret >= 0) { + *value = data; + ret = 0; + } + return ret; +} + + +static int ov7670_write_i2c(struct v4l2_subdev *sd, unsigned char reg, + unsigned char value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct i2c_msg msg; + unsigned char data[2] = { reg, value }; + int ret; + + msg.addr = client->addr; + msg.flags = 0; + msg.len = 2; + msg.buf = data; + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret > 0) + ret = 0; + if (reg == REG_COM7 && (value & COM7_RESET)) + msleep(5); /* Wait for reset to run */ + return ret; +} + +static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, + unsigned char *value) +{ + struct ov7670_info *info = to_state(sd); + if (info->use_smbus) + return ov7670_read_smbus(sd, reg, value); + else + return ov7670_read_i2c(sd, reg, value); +} + +static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, + unsigned char value) +{ + struct ov7670_info *info = to_state(sd); + if (info->use_smbus) + return ov7670_write_smbus(sd, reg, value); + else + return ov7670_write_i2c(sd, reg, value); +} + +/* + * Write a list of register settings; ff/ff stops the process. + */ +static int ov7670_write_array(struct v4l2_subdev *sd, struct regval_list *vals) +{ + while (vals->reg_num != 0xff || vals->value != 0xff) { + int ret = ov7670_write(sd, vals->reg_num, vals->value); + if (ret < 0) + return ret; + vals++; + } + return 0; +} + + +/* + * Stuff that knows about the sensor. + */ +static int ov7670_reset(struct v4l2_subdev *sd, u32 val) +{ + ov7670_write(sd, REG_COM7, COM7_RESET); + msleep(1); + return 0; +} + + +static int ov7670_init(struct v4l2_subdev *sd, u32 val) +{ + return ov7670_write_array(sd, ov7670_default_regs); +} + + + +static int ov7670_detect(struct v4l2_subdev *sd) +{ + unsigned char v; + int ret; + + ret = ov7670_init(sd, 0); + if (ret < 0) + return ret; + ret = ov7670_read(sd, REG_MIDH, &v); + if (ret < 0) + return ret; + if (v != 0x7f) /* OV manuf. id. */ + return -ENODEV; + ret = ov7670_read(sd, REG_MIDL, &v); + if (ret < 0) + return ret; + if (v != 0xa2) + return -ENODEV; + /* + * OK, we know we have an OmniVision chip...but which one? + */ + ret = ov7670_read(sd, REG_PID, &v); + if (ret < 0) + return ret; + if (v != 0x76) /* PID + VER = 0x76 / 0x73 */ + return -ENODEV; + ret = ov7670_read(sd, REG_VER, &v); + if (ret < 0) + return ret; + if (v != 0x73) /* PID + VER = 0x76 / 0x73 */ + return -ENODEV; + return 0; +} + + +/* + * Store information about the video data format. The color matrix + * is deeply tied into the format, so keep the relevant values here. + * The magic matrix numbers come from OmniVision. + */ +static struct ov7670_format_struct { + enum v4l2_mbus_pixelcode mbus_code; + enum v4l2_colorspace colorspace; + struct regval_list *regs; + int cmatrix[CMATRIX_LEN]; +} ov7670_formats[] = { + { + .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .regs = ov7670_fmt_yuv422, + .cmatrix = { 128, -128, 0, -34, -94, 128 }, + }, + { + .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE, + .colorspace = V4L2_COLORSPACE_SRGB, + .regs = ov7670_fmt_rgb444, + .cmatrix = { 179, -179, 0, -61, -176, 228 }, + }, + { + .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE, + .colorspace = V4L2_COLORSPACE_SRGB, + .regs = ov7670_fmt_rgb565, + .cmatrix = { 179, -179, 0, -61, -176, 228 }, + }, + { + .mbus_code = V4L2_MBUS_FMT_SBGGR8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .regs = ov7670_fmt_raw, + .cmatrix = { 0, 0, 0, 0, 0, 0 }, + }, +}; +#define N_OV7670_FMTS ARRAY_SIZE(ov7670_formats) + + +/* + * Then there is the issue of window sizes. Try to capture the info here. + */ + +/* + * QCIF mode is done (by OV) in a very strange way - it actually looks like + * VGA with weird scaling options - they do *not* use the canned QCIF mode + * which is allegedly provided by the sensor. So here's the weird register + * settings. + */ +static struct regval_list ov7670_qcif_regs[] = { + { REG_COM3, COM3_SCALEEN|COM3_DCWEN }, + { REG_COM3, COM3_DCWEN }, + { REG_COM14, COM14_DCWEN | 0x01}, + { 0x73, 0xf1 }, + { 0xa2, 0x52 }, + { 0x7b, 0x1c }, + { 0x7c, 0x28 }, + { 0x7d, 0x3c }, + { 0x7f, 0x69 }, + { REG_COM9, 0x38 }, + { 0xa1, 0x0b }, + { 0x74, 0x19 }, + { 0x9a, 0x80 }, + { 0x43, 0x14 }, + { REG_COM13, 0xc0 }, + { 0xff, 0xff }, +}; + +static struct ov7670_win_size { + int width; + int height; + unsigned char com7_bit; + int hstart; /* Start/stop values for the camera. Note */ + int hstop; /* that they do not always make complete */ + int vstart; /* sense to humans, but evidently the sensor */ + int vstop; /* will do the right thing... */ + struct regval_list *regs; /* Regs to tweak */ +/* h/vref stuff */ +} ov7670_win_sizes[] = { + /* VGA */ + { + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .com7_bit = COM7_FMT_VGA, + .hstart = 158, /* These values from */ + .hstop = 14, /* Omnivision */ + .vstart = 10, + .vstop = 490, + .regs = NULL, + }, + /* CIF */ + { + .width = CIF_WIDTH, + .height = CIF_HEIGHT, + .com7_bit = COM7_FMT_CIF, + .hstart = 170, /* Empirically determined */ + .hstop = 90, + .vstart = 14, + .vstop = 494, + .regs = NULL, + }, + /* QVGA */ + { + .width = QVGA_WIDTH, + .height = QVGA_HEIGHT, + .com7_bit = COM7_FMT_QVGA, + .hstart = 168, /* Empirically determined */ + .hstop = 24, + .vstart = 12, + .vstop = 492, + .regs = NULL, + }, + /* QCIF */ + { + .width = QCIF_WIDTH, + .height = QCIF_HEIGHT, + .com7_bit = COM7_FMT_VGA, /* see comment above */ + .hstart = 456, /* Empirically determined */ + .hstop = 24, + .vstart = 14, + .vstop = 494, + .regs = ov7670_qcif_regs, + }, +}; + +#define N_WIN_SIZES (ARRAY_SIZE(ov7670_win_sizes)) + + +/* + * Store a set of start/stop values into the camera. + */ +static int ov7670_set_hw(struct v4l2_subdev *sd, int hstart, int hstop, + int vstart, int vstop) +{ + int ret; + unsigned char v; +/* + * Horizontal: 11 bits, top 8 live in hstart and hstop. Bottom 3 of + * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is + * a mystery "edge offset" value in the top two bits of href. + */ + ret = ov7670_write(sd, REG_HSTART, (hstart >> 3) & 0xff); + ret += ov7670_write(sd, REG_HSTOP, (hstop >> 3) & 0xff); + ret += ov7670_read(sd, REG_HREF, &v); + v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7); + msleep(10); + ret += ov7670_write(sd, REG_HREF, v); +/* + * Vertical: similar arrangement, but only 10 bits. + */ + ret += ov7670_write(sd, REG_VSTART, (vstart >> 2) & 0xff); + ret += ov7670_write(sd, REG_VSTOP, (vstop >> 2) & 0xff); + ret += ov7670_read(sd, REG_VREF, &v); + v = (v & 0xf0) | ((vstop & 0x3) << 2) | (vstart & 0x3); + msleep(10); + ret += ov7670_write(sd, REG_VREF, v); + return ret; +} + + +static int ov7670_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, + enum v4l2_mbus_pixelcode *code) +{ + if (index >= N_OV7670_FMTS) + return -EINVAL; + + *code = ov7670_formats[index].mbus_code; + return 0; +} + +static int ov7670_try_fmt_internal(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt, + struct ov7670_format_struct **ret_fmt, + struct ov7670_win_size **ret_wsize) +{ + int index; + struct ov7670_win_size *wsize; + + for (index = 0; index < N_OV7670_FMTS; index++) + if (ov7670_formats[index].mbus_code == fmt->code) + break; + if (index >= N_OV7670_FMTS) { + /* default to first format */ + index = 0; + fmt->code = ov7670_formats[0].mbus_code; + } + if (ret_fmt != NULL) + *ret_fmt = ov7670_formats + index; + /* + * Fields: the OV devices claim to be progressive. + */ + fmt->field = V4L2_FIELD_NONE; + /* + * Round requested image size down to the nearest + * we support, but not below the smallest. + */ + for (wsize = ov7670_win_sizes; wsize < ov7670_win_sizes + N_WIN_SIZES; + wsize++) + if (fmt->width >= wsize->width && fmt->height >= wsize->height) + break; + if (wsize >= ov7670_win_sizes + N_WIN_SIZES) + wsize--; /* Take the smallest one */ + if (ret_wsize != NULL) + *ret_wsize = wsize; + /* + * Note the size we'll actually handle. + */ + fmt->width = wsize->width; + fmt->height = wsize->height; + fmt->colorspace = ov7670_formats[index].colorspace; + return 0; +} + +static int ov7670_try_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + return ov7670_try_fmt_internal(sd, fmt, NULL, NULL); +} + +/* + * Set a format. + */ +static int ov7670_s_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct ov7670_format_struct *ovfmt; + struct ov7670_win_size *wsize; + struct ov7670_info *info = to_state(sd); + unsigned char com7; + int ret; + + ret = ov7670_try_fmt_internal(sd, fmt, &ovfmt, &wsize); + + if (ret) + return ret; + /* + * COM7 is a pain in the ass, it doesn't like to be read then + * quickly written afterward. But we have everything we need + * to set it absolutely here, as long as the format-specific + * register sets list it first. + */ + com7 = ovfmt->regs[0].value; + com7 |= wsize->com7_bit; + ov7670_write(sd, REG_COM7, com7); + /* + * Now write the rest of the array. Also store start/stops + */ + ov7670_write_array(sd, ovfmt->regs + 1); + ov7670_set_hw(sd, wsize->hstart, wsize->hstop, wsize->vstart, + wsize->vstop); + ret = 0; + if (wsize->regs) + ret = ov7670_write_array(sd, wsize->regs); + info->fmt = ovfmt; + + /* + * If we're running RGB565, we must rewrite clkrc after setting + * the other parameters or the image looks poor. If we're *not* + * doing RGB565, we must not rewrite clkrc or the image looks + * *really* poor. + * + * (Update) Now that we retain clkrc state, we should be able + * to write it unconditionally, and that will make the frame + * rate persistent too. + */ + if (ret == 0) + ret = ov7670_write(sd, REG_CLKRC, info->clkrc); + return 0; +} + +/* + * Implement G/S_PARM. There is a "high quality" mode we could try + * to do someday; for now, we just do the frame rate tweak. + */ +static int ov7670_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + struct ov7670_info *info = to_state(sd); + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memset(cp, 0, sizeof(struct v4l2_captureparm)); + cp->capability = V4L2_CAP_TIMEPERFRAME; + cp->timeperframe.numerator = 1; + cp->timeperframe.denominator = info->clock_speed; + if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1) + cp->timeperframe.denominator /= (info->clkrc & CLK_SCALE); + return 0; +} + +static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + struct v4l2_fract *tpf = &cp->timeperframe; + struct ov7670_info *info = to_state(sd); + int div; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (cp->extendedmode != 0) + return -EINVAL; + + if (tpf->numerator == 0 || tpf->denominator == 0) + div = 1; /* Reset to full rate */ + else + div = (tpf->numerator * info->clock_speed) / tpf->denominator; + if (div == 0) + div = 1; + else if (div > CLK_SCALE) + div = CLK_SCALE; + info->clkrc = (info->clkrc & 0x80) | div; + tpf->numerator = 1; + tpf->denominator = info->clock_speed / div; + return ov7670_write(sd, REG_CLKRC, info->clkrc); +} + + +/* + * Frame intervals. Since frame rates are controlled with the clock + * divider, we can only do 30/n for integer n values. So no continuous + * or stepwise options. Here we just pick a handful of logical values. + */ + +static int ov7670_frame_rates[] = { 30, 15, 10, 5, 1 }; + +static int ov7670_enum_frameintervals(struct v4l2_subdev *sd, + struct v4l2_frmivalenum *interval) +{ + if (interval->index >= ARRAY_SIZE(ov7670_frame_rates)) + return -EINVAL; + interval->type = V4L2_FRMIVAL_TYPE_DISCRETE; + interval->discrete.numerator = 1; + interval->discrete.denominator = ov7670_frame_rates[interval->index]; + return 0; +} + +/* + * Frame size enumeration + */ +static int ov7670_enum_framesizes(struct v4l2_subdev *sd, + struct v4l2_frmsizeenum *fsize) +{ + struct ov7670_info *info = to_state(sd); + int i; + int num_valid = -1; + __u32 index = fsize->index; + + /* + * If a minimum width/height was requested, filter out the capture + * windows that fall outside that. + */ + for (i = 0; i < N_WIN_SIZES; i++) { + struct ov7670_win_size *win = &ov7670_win_sizes[index]; + if (info->min_width && win->width < info->min_width) + continue; + if (info->min_height && win->height < info->min_height) + continue; + if (index == ++num_valid) { + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = win->width; + fsize->discrete.height = win->height; + return 0; + } + } + + return -EINVAL; +} + +/* + * Code for dealing with controls. + */ + +static int ov7670_store_cmatrix(struct v4l2_subdev *sd, + int matrix[CMATRIX_LEN]) +{ + int i, ret; + unsigned char signbits = 0; + + /* + * Weird crap seems to exist in the upper part of + * the sign bits register, so let's preserve it. + */ + ret = ov7670_read(sd, REG_CMATRIX_SIGN, &signbits); + signbits &= 0xc0; + + for (i = 0; i < CMATRIX_LEN; i++) { + unsigned char raw; + + if (matrix[i] < 0) { + signbits |= (1 << i); + if (matrix[i] < -255) + raw = 0xff; + else + raw = (-1 * matrix[i]) & 0xff; + } + else { + if (matrix[i] > 255) + raw = 0xff; + else + raw = matrix[i] & 0xff; + } + ret += ov7670_write(sd, REG_CMATRIX_BASE + i, raw); + } + ret += ov7670_write(sd, REG_CMATRIX_SIGN, signbits); + return ret; +} + + +/* + * Hue also requires messing with the color matrix. It also requires + * trig functions, which tend not to be well supported in the kernel. + * So here is a simple table of sine values, 0-90 degrees, in steps + * of five degrees. Values are multiplied by 1000. + * + * The following naive approximate trig functions require an argument + * carefully limited to -180 <= theta <= 180. + */ +#define SIN_STEP 5 +static const int ov7670_sin_table[] = { + 0, 87, 173, 258, 342, 422, + 499, 573, 642, 707, 766, 819, + 866, 906, 939, 965, 984, 996, + 1000 +}; + +static int ov7670_sine(int theta) +{ + int chs = 1; + int sine; + + if (theta < 0) { + theta = -theta; + chs = -1; + } + if (theta <= 90) + sine = ov7670_sin_table[theta/SIN_STEP]; + else { + theta -= 90; + sine = 1000 - ov7670_sin_table[theta/SIN_STEP]; + } + return sine*chs; +} + +static int ov7670_cosine(int theta) +{ + theta = 90 - theta; + if (theta > 180) + theta -= 360; + else if (theta < -180) + theta += 360; + return ov7670_sine(theta); +} + + + + +static void ov7670_calc_cmatrix(struct ov7670_info *info, + int matrix[CMATRIX_LEN]) +{ + int i; + /* + * Apply the current saturation setting first. + */ + for (i = 0; i < CMATRIX_LEN; i++) + matrix[i] = (info->fmt->cmatrix[i]*info->sat) >> 7; + /* + * Then, if need be, rotate the hue value. + */ + if (info->hue != 0) { + int sinth, costh, tmpmatrix[CMATRIX_LEN]; + + memcpy(tmpmatrix, matrix, CMATRIX_LEN*sizeof(int)); + sinth = ov7670_sine(info->hue); + costh = ov7670_cosine(info->hue); + + matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000; + matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000; + matrix[2] = (matrix[5]*sinth + matrix[2]*costh)/1000; + matrix[3] = (matrix[3]*costh - matrix[0]*sinth)/1000; + matrix[4] = (matrix[4]*costh - matrix[1]*sinth)/1000; + matrix[5] = (matrix[5]*costh - matrix[2]*sinth)/1000; + } +} + + + +static int ov7670_s_sat(struct v4l2_subdev *sd, int value) +{ + struct ov7670_info *info = to_state(sd); + int matrix[CMATRIX_LEN]; + int ret; + + info->sat = value; + ov7670_calc_cmatrix(info, matrix); + ret = ov7670_store_cmatrix(sd, matrix); + return ret; +} + +static int ov7670_g_sat(struct v4l2_subdev *sd, __s32 *value) +{ + struct ov7670_info *info = to_state(sd); + + *value = info->sat; + return 0; +} + +static int ov7670_s_hue(struct v4l2_subdev *sd, int value) +{ + struct ov7670_info *info = to_state(sd); + int matrix[CMATRIX_LEN]; + int ret; + + if (value < -180 || value > 180) + return -EINVAL; + info->hue = value; + ov7670_calc_cmatrix(info, matrix); + ret = ov7670_store_cmatrix(sd, matrix); + return ret; +} + + +static int ov7670_g_hue(struct v4l2_subdev *sd, __s32 *value) +{ + struct ov7670_info *info = to_state(sd); + + *value = info->hue; + return 0; +} + + +/* + * Some weird registers seem to store values in a sign/magnitude format! + */ +static unsigned char ov7670_sm_to_abs(unsigned char v) +{ + if ((v & 0x80) == 0) + return v + 128; + return 128 - (v & 0x7f); +} + + +static unsigned char ov7670_abs_to_sm(unsigned char v) +{ + if (v > 127) + return v & 0x7f; + return (128 - v) | 0x80; +} + +static int ov7670_s_brightness(struct v4l2_subdev *sd, int value) +{ + unsigned char com8 = 0, v; + int ret; + + ov7670_read(sd, REG_COM8, &com8); + com8 &= ~COM8_AEC; + ov7670_write(sd, REG_COM8, com8); + v = ov7670_abs_to_sm(value); + ret = ov7670_write(sd, REG_BRIGHT, v); + return ret; +} + +static int ov7670_g_brightness(struct v4l2_subdev *sd, __s32 *value) +{ + unsigned char v = 0; + int ret = ov7670_read(sd, REG_BRIGHT, &v); + + *value = ov7670_sm_to_abs(v); + return ret; +} + +static int ov7670_s_contrast(struct v4l2_subdev *sd, int value) +{ + return ov7670_write(sd, REG_CONTRAS, (unsigned char) value); +} + +static int ov7670_g_contrast(struct v4l2_subdev *sd, __s32 *value) +{ + unsigned char v = 0; + int ret = ov7670_read(sd, REG_CONTRAS, &v); + + *value = v; + return ret; +} + +static int ov7670_g_hflip(struct v4l2_subdev *sd, __s32 *value) +{ + int ret; + unsigned char v = 0; + + ret = ov7670_read(sd, REG_MVFP, &v); + *value = (v & MVFP_MIRROR) == MVFP_MIRROR; + return ret; +} + + +static int ov7670_s_hflip(struct v4l2_subdev *sd, int value) +{ + unsigned char v = 0; + int ret; + + ret = ov7670_read(sd, REG_MVFP, &v); + if (value) + v |= MVFP_MIRROR; + else + v &= ~MVFP_MIRROR; + msleep(10); /* FIXME */ + ret += ov7670_write(sd, REG_MVFP, v); + return ret; +} + + + +static int ov7670_g_vflip(struct v4l2_subdev *sd, __s32 *value) +{ + int ret; + unsigned char v = 0; + + ret = ov7670_read(sd, REG_MVFP, &v); + *value = (v & MVFP_FLIP) == MVFP_FLIP; + return ret; +} + + +static int ov7670_s_vflip(struct v4l2_subdev *sd, int value) +{ + unsigned char v = 0; + int ret; + + ret = ov7670_read(sd, REG_MVFP, &v); + if (value) + v |= MVFP_FLIP; + else + v &= ~MVFP_FLIP; + msleep(10); /* FIXME */ + ret += ov7670_write(sd, REG_MVFP, v); + return ret; +} + +/* + * GAIN is split between REG_GAIN and REG_VREF[7:6]. If one believes + * the data sheet, the VREF parts should be the most significant, but + * experience shows otherwise. There seems to be little value in + * messing with the VREF bits, so we leave them alone. + */ +static int ov7670_g_gain(struct v4l2_subdev *sd, __s32 *value) +{ + int ret; + unsigned char gain; + + ret = ov7670_read(sd, REG_GAIN, &gain); + *value = gain; + return ret; +} + +static int ov7670_s_gain(struct v4l2_subdev *sd, int value) +{ + int ret; + unsigned char com8; + + ret = ov7670_write(sd, REG_GAIN, value & 0xff); + /* Have to turn off AGC as well */ + if (ret == 0) { + ret = ov7670_read(sd, REG_COM8, &com8); + ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AGC); + } + return ret; +} + +/* + * Tweak autogain. + */ +static int ov7670_g_autogain(struct v4l2_subdev *sd, __s32 *value) +{ + int ret; + unsigned char com8; + + ret = ov7670_read(sd, REG_COM8, &com8); + *value = (com8 & COM8_AGC) != 0; + return ret; +} + +static int ov7670_s_autogain(struct v4l2_subdev *sd, int value) +{ + int ret; + unsigned char com8; + + ret = ov7670_read(sd, REG_COM8, &com8); + if (ret == 0) { + if (value) + com8 |= COM8_AGC; + else + com8 &= ~COM8_AGC; + ret = ov7670_write(sd, REG_COM8, com8); + } + return ret; +} + +/* + * Exposure is spread all over the place: top 6 bits in AECHH, middle + * 8 in AECH, and two stashed in COM1 just for the hell of it. + */ +static int ov7670_g_exp(struct v4l2_subdev *sd, __s32 *value) +{ + int ret; + unsigned char com1, aech, aechh; + + ret = ov7670_read(sd, REG_COM1, &com1) + + ov7670_read(sd, REG_AECH, &aech) + + ov7670_read(sd, REG_AECHH, &aechh); + *value = ((aechh & 0x3f) << 10) | (aech << 2) | (com1 & 0x03); + return ret; +} + +static int ov7670_s_exp(struct v4l2_subdev *sd, int value) +{ + int ret; + unsigned char com1, com8, aech, aechh; + + ret = ov7670_read(sd, REG_COM1, &com1) + + ov7670_read(sd, REG_COM8, &com8); + ov7670_read(sd, REG_AECHH, &aechh); + if (ret) + return ret; + + com1 = (com1 & 0xfc) | (value & 0x03); + aech = (value >> 2) & 0xff; + aechh = (aechh & 0xc0) | ((value >> 10) & 0x3f); + ret = ov7670_write(sd, REG_COM1, com1) + + ov7670_write(sd, REG_AECH, aech) + + ov7670_write(sd, REG_AECHH, aechh); + /* Have to turn off AEC as well */ + if (ret == 0) + ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AEC); + return ret; +} + +/* + * Tweak autoexposure. + */ +static int ov7670_g_autoexp(struct v4l2_subdev *sd, __s32 *value) +{ + int ret; + unsigned char com8; + enum v4l2_exposure_auto_type *atype = (enum v4l2_exposure_auto_type *) value; + + ret = ov7670_read(sd, REG_COM8, &com8); + if (com8 & COM8_AEC) + *atype = V4L2_EXPOSURE_AUTO; + else + *atype = V4L2_EXPOSURE_MANUAL; + return ret; +} + +static int ov7670_s_autoexp(struct v4l2_subdev *sd, + enum v4l2_exposure_auto_type value) +{ + int ret; + unsigned char com8; + + ret = ov7670_read(sd, REG_COM8, &com8); + if (ret == 0) { + if (value == V4L2_EXPOSURE_AUTO) + com8 |= COM8_AEC; + else + com8 &= ~COM8_AEC; + ret = ov7670_write(sd, REG_COM8, com8); + } + return ret; +} + + + +static int ov7670_queryctrl(struct v4l2_subdev *sd, + struct v4l2_queryctrl *qc) +{ + /* Fill in min, max, step and default value for these controls. */ + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); + case V4L2_CID_CONTRAST: + return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64); + case V4L2_CID_VFLIP: + case V4L2_CID_HFLIP: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); + case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(qc, 0, 256, 1, 128); + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill(qc, -180, 180, 5, 0); + case V4L2_CID_GAIN: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); + case V4L2_CID_AUTOGAIN: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); + case V4L2_CID_EXPOSURE: + return v4l2_ctrl_query_fill(qc, 0, 65535, 1, 500); + case V4L2_CID_EXPOSURE_AUTO: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); + } + return -EINVAL; +} + +static int ov7670_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + return ov7670_g_brightness(sd, &ctrl->value); + case V4L2_CID_CONTRAST: + return ov7670_g_contrast(sd, &ctrl->value); + case V4L2_CID_SATURATION: + return ov7670_g_sat(sd, &ctrl->value); + case V4L2_CID_HUE: + return ov7670_g_hue(sd, &ctrl->value); + case V4L2_CID_VFLIP: + return ov7670_g_vflip(sd, &ctrl->value); + case V4L2_CID_HFLIP: + return ov7670_g_hflip(sd, &ctrl->value); + case V4L2_CID_GAIN: + return ov7670_g_gain(sd, &ctrl->value); + case V4L2_CID_AUTOGAIN: + return ov7670_g_autogain(sd, &ctrl->value); + case V4L2_CID_EXPOSURE: + return ov7670_g_exp(sd, &ctrl->value); + case V4L2_CID_EXPOSURE_AUTO: + return ov7670_g_autoexp(sd, &ctrl->value); + } + return -EINVAL; +} + +static int ov7670_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + return ov7670_s_brightness(sd, ctrl->value); + case V4L2_CID_CONTRAST: + return ov7670_s_contrast(sd, ctrl->value); + case V4L2_CID_SATURATION: + return ov7670_s_sat(sd, ctrl->value); + case V4L2_CID_HUE: + return ov7670_s_hue(sd, ctrl->value); + case V4L2_CID_VFLIP: + return ov7670_s_vflip(sd, ctrl->value); + case V4L2_CID_HFLIP: + return ov7670_s_hflip(sd, ctrl->value); + case V4L2_CID_GAIN: + return ov7670_s_gain(sd, ctrl->value); + case V4L2_CID_AUTOGAIN: + return ov7670_s_autogain(sd, ctrl->value); + case V4L2_CID_EXPOSURE: + return ov7670_s_exp(sd, ctrl->value); + case V4L2_CID_EXPOSURE_AUTO: + return ov7670_s_autoexp(sd, + (enum v4l2_exposure_auto_type) ctrl->value); + } + return -EINVAL; +} + +static int ov7670_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_OV7670, 0); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + unsigned char val = 0; + int ret; + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + ret = ov7670_read(sd, reg->reg & 0xff, &val); + reg->val = val; + reg->size = 1; + return ret; +} + +static int ov7670_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + ov7670_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops ov7670_core_ops = { + .g_chip_ident = ov7670_g_chip_ident, + .g_ctrl = ov7670_g_ctrl, + .s_ctrl = ov7670_s_ctrl, + .queryctrl = ov7670_queryctrl, + .reset = ov7670_reset, + .init = ov7670_init, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = ov7670_g_register, + .s_register = ov7670_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops ov7670_video_ops = { + .enum_mbus_fmt = ov7670_enum_mbus_fmt, + .try_mbus_fmt = ov7670_try_mbus_fmt, + .s_mbus_fmt = ov7670_s_mbus_fmt, + .s_parm = ov7670_s_parm, + .g_parm = ov7670_g_parm, + .enum_frameintervals = ov7670_enum_frameintervals, + .enum_framesizes = ov7670_enum_framesizes, +}; + +static const struct v4l2_subdev_ops ov7670_ops = { + .core = &ov7670_core_ops, + .video = &ov7670_video_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static int ov7670_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct v4l2_subdev *sd; + struct ov7670_info *info; + int ret; + + info = kzalloc(sizeof(struct ov7670_info), GFP_KERNEL); + if (info == NULL) + return -ENOMEM; + sd = &info->sd; + v4l2_i2c_subdev_init(sd, client, &ov7670_ops); + + info->clock_speed = 30; /* default: a guess */ + if (client->dev.platform_data) { + struct ov7670_config *config = client->dev.platform_data; + + /* + * Must apply configuration before initializing device, because it + * selects I/O method. + */ + info->min_width = config->min_width; + info->min_height = config->min_height; + info->use_smbus = config->use_smbus; + + if (config->clock_speed) + info->clock_speed = config->clock_speed; + } + + /* Make sure it's an ov7670 */ + ret = ov7670_detect(sd); + if (ret) { + v4l_dbg(1, debug, client, + "chip found @ 0x%x (%s) is not an ov7670 chip.\n", + client->addr << 1, client->adapter->name); + kfree(info); + return ret; + } + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + info->fmt = &ov7670_formats[0]; + info->sat = 128; /* Review this */ + info->clkrc = info->clock_speed / 30; + return 0; +} + + +static int ov7670_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 ov7670_id[] = { + { "ov7670", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ov7670_id); + +static struct i2c_driver ov7670_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "ov7670", + }, + .probe = ov7670_probe, + .remove = ov7670_remove, + .id_table = ov7670_id, +}; + +module_i2c_driver(ov7670_driver); diff --git a/drivers/media/i2c/s5k6aa.c b/drivers/media/i2c/s5k6aa.c new file mode 100644 index 00000000000..045ca7f4f6c --- /dev/null +++ b/drivers/media/i2c/s5k6aa.c @@ -0,0 +1,1667 @@ +/* + * Driver for Samsung S5K6AAFX SXGA 1/6" 1.3M CMOS Image Sensor + * with embedded SoC ISP. + * + * Copyright (C) 2011, Samsung Electronics Co., Ltd. + * Sylwester Nawrocki + * + * Based on a driver authored by Dongsoo Nathaniel Kim. + * Copyright (C) 2009, Dongsoo Nathaniel Kim + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static int debug; +module_param(debug, int, 0644); + +#define DRIVER_NAME "S5K6AA" + +/* The token to indicate array termination */ +#define S5K6AA_TERM 0xffff +#define S5K6AA_OUT_WIDTH_DEF 640 +#define S5K6AA_OUT_HEIGHT_DEF 480 +#define S5K6AA_WIN_WIDTH_MAX 1280 +#define S5K6AA_WIN_HEIGHT_MAX 1024 +#define S5K6AA_WIN_WIDTH_MIN 8 +#define S5K6AA_WIN_HEIGHT_MIN 8 + +/* + * H/W register Interface (0xD0000000 - 0xD0000FFF) + */ +#define AHB_MSB_ADDR_PTR 0xfcfc +#define GEN_REG_OFFSH 0xd000 +#define REG_CMDWR_ADDRH 0x0028 +#define REG_CMDWR_ADDRL 0x002a +#define REG_CMDRD_ADDRH 0x002c +#define REG_CMDRD_ADDRL 0x002e +#define REG_CMDBUF0_ADDR 0x0f12 +#define REG_CMDBUF1_ADDR 0x0f10 + +/* + * Host S/W Register interface (0x70000000 - 0x70002000) + * The value of the two most significant address bytes is 0x7000, + * (HOST_SWIF_OFFS_H). The register addresses below specify 2 LSBs. + */ +#define HOST_SWIF_OFFSH 0x7000 + +/* Initialization parameters */ +/* Master clock frequency in KHz */ +#define REG_I_INCLK_FREQ_L 0x01b8 +#define REG_I_INCLK_FREQ_H 0x01ba +#define MIN_MCLK_FREQ_KHZ 6000U +#define MAX_MCLK_FREQ_KHZ 27000U +#define REG_I_USE_NPVI_CLOCKS 0x01c6 +#define REG_I_USE_NMIPI_CLOCKS 0x01c8 + +/* Clock configurations, n = 0..2. REG_I_* frequency unit is 4 kHz. */ +#define REG_I_OPCLK_4KHZ(n) ((n) * 6 + 0x01cc) +#define REG_I_MIN_OUTRATE_4KHZ(n) ((n) * 6 + 0x01ce) +#define REG_I_MAX_OUTRATE_4KHZ(n) ((n) * 6 + 0x01d0) +#define SYS_PLL_OUT_FREQ (48000000 / 4000) +#define PCLK_FREQ_MIN (24000000 / 4000) +#define PCLK_FREQ_MAX (48000000 / 4000) +#define REG_I_INIT_PARAMS_UPDATED 0x01e0 +#define REG_I_ERROR_INFO 0x01e2 + +/* General purpose parameters */ +#define REG_USER_BRIGHTNESS 0x01e4 +#define REG_USER_CONTRAST 0x01e6 +#define REG_USER_SATURATION 0x01e8 +#define REG_USER_SHARPBLUR 0x01ea + +#define REG_G_SPEC_EFFECTS 0x01ee +#define REG_G_ENABLE_PREV 0x01f0 +#define REG_G_ENABLE_PREV_CHG 0x01f2 +#define REG_G_NEW_CFG_SYNC 0x01f8 +#define REG_G_PREVZOOM_IN_WIDTH 0x020a +#define REG_G_PREVZOOM_IN_HEIGHT 0x020c +#define REG_G_PREVZOOM_IN_XOFFS 0x020e +#define REG_G_PREVZOOM_IN_YOFFS 0x0210 +#define REG_G_INPUTS_CHANGE_REQ 0x021a +#define REG_G_ACTIVE_PREV_CFG 0x021c +#define REG_G_PREV_CFG_CHG 0x021e +#define REG_G_PREV_OPEN_AFTER_CH 0x0220 +#define REG_G_PREV_CFG_ERROR 0x0222 + +/* Preview control section. n = 0...4. */ +#define PREG(n, x) ((n) * 0x26 + x) +#define REG_P_OUT_WIDTH(n) PREG(n, 0x0242) +#define REG_P_OUT_HEIGHT(n) PREG(n, 0x0244) +#define REG_P_FMT(n) PREG(n, 0x0246) +#define REG_P_MAX_OUT_RATE(n) PREG(n, 0x0248) +#define REG_P_MIN_OUT_RATE(n) PREG(n, 0x024a) +#define REG_P_PVI_MASK(n) PREG(n, 0x024c) +#define REG_P_CLK_INDEX(n) PREG(n, 0x024e) +#define REG_P_FR_RATE_TYPE(n) PREG(n, 0x0250) +#define FR_RATE_DYNAMIC 0 +#define FR_RATE_FIXED 1 +#define FR_RATE_FIXED_ACCURATE 2 +#define REG_P_FR_RATE_Q_TYPE(n) PREG(n, 0x0252) +#define FR_RATE_Q_BEST_FRRATE 1 /* Binning enabled */ +#define FR_RATE_Q_BEST_QUALITY 2 /* Binning disabled */ +/* Frame period in 0.1 ms units */ +#define REG_P_MAX_FR_TIME(n) PREG(n, 0x0254) +#define REG_P_MIN_FR_TIME(n) PREG(n, 0x0256) +/* Conversion to REG_P_[MAX/MIN]_FR_TIME value; __t: time in us */ +#define US_TO_FR_TIME(__t) ((__t) / 100) +#define S5K6AA_MIN_FR_TIME 33300 /* us */ +#define S5K6AA_MAX_FR_TIME 650000 /* us */ +#define S5K6AA_MAX_HIGHRES_FR_TIME 666 /* x100 us */ +/* The below 5 registers are for "device correction" values */ +#define REG_P_COLORTEMP(n) PREG(n, 0x025e) +#define REG_P_PREV_MIRROR(n) PREG(n, 0x0262) + +/* Extended image property controls */ +/* Exposure time in 10 us units */ +#define REG_SF_USR_EXPOSURE_L 0x03c6 +#define REG_SF_USR_EXPOSURE_H 0x03c8 +#define REG_SF_USR_EXPOSURE_CHG 0x03ca +#define REG_SF_USR_TOT_GAIN 0x03cc +#define REG_SF_USR_TOT_GAIN_CHG 0x03ce +#define REG_SF_RGAIN 0x03d0 +#define REG_SF_RGAIN_CHG 0x03d2 +#define REG_SF_GGAIN 0x03d4 +#define REG_SF_GGAIN_CHG 0x03d6 +#define REG_SF_BGAIN 0x03d8 +#define REG_SF_BGAIN_CHG 0x03da +#define REG_SF_FLICKER_QUANT 0x03dc +#define REG_SF_FLICKER_QUANT_CHG 0x03de + +/* Output interface (parallel/MIPI) setup */ +#define REG_OIF_EN_MIPI_LANES 0x03fa +#define REG_OIF_EN_PACKETS 0x03fc +#define REG_OIF_CFG_CHG 0x03fe + +/* Auto-algorithms enable mask */ +#define REG_DBG_AUTOALG_EN 0x0400 +#define AALG_ALL_EN_MASK (1 << 0) +#define AALG_AE_EN_MASK (1 << 1) +#define AALG_DIVLEI_EN_MASK (1 << 2) +#define AALG_WB_EN_MASK (1 << 3) +#define AALG_FLICKER_EN_MASK (1 << 5) +#define AALG_FIT_EN_MASK (1 << 6) +#define AALG_WRHW_EN_MASK (1 << 7) + +/* Firmware revision information */ +#define REG_FW_APIVER 0x012e +#define S5K6AAFX_FW_APIVER 0x0001 +#define REG_FW_REVISION 0x0130 + +/* For now we use only one user configuration register set */ +#define S5K6AA_MAX_PRESETS 1 + +static const char * const s5k6aa_supply_names[] = { + "vdd_core", /* Digital core supply 1.5V (1.4V to 1.6V) */ + "vdda", /* Analog power supply 2.8V (2.6V to 3.0V) */ + "vdd_reg", /* Regulator input power 1.8V (1.7V to 1.9V) + or 2.8V (2.6V to 3.0) */ + "vddio", /* I/O supply 1.8V (1.65V to 1.95V) + or 2.8V (2.5V to 3.1V) */ +}; +#define S5K6AA_NUM_SUPPLIES ARRAY_SIZE(s5k6aa_supply_names) + +enum s5k6aa_gpio_id { + STBY, + RST, + GPIO_NUM, +}; + +struct s5k6aa_regval { + u16 addr; + u16 val; +}; + +struct s5k6aa_pixfmt { + enum v4l2_mbus_pixelcode code; + u32 colorspace; + /* REG_P_FMT(x) register value */ + u16 reg_p_fmt; +}; + +struct s5k6aa_preset { + /* output pixel format and resolution */ + struct v4l2_mbus_framefmt mbus_fmt; + u8 clk_id; + u8 index; +}; + +struct s5k6aa_ctrls { + struct v4l2_ctrl_handler handler; + /* Auto / manual white balance cluster */ + struct v4l2_ctrl *awb; + struct v4l2_ctrl *gain_red; + struct v4l2_ctrl *gain_blue; + struct v4l2_ctrl *gain_green; + /* Mirror cluster */ + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + /* Auto exposure / manual exposure and gain cluster */ + struct v4l2_ctrl *auto_exp; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; +}; + +struct s5k6aa_interval { + u16 reg_fr_time; + struct v4l2_fract interval; + /* Maximum rectangle for the interval */ + struct v4l2_frmsize_discrete size; +}; + +struct s5k6aa { + struct v4l2_subdev sd; + struct media_pad pad; + + enum v4l2_mbus_type bus_type; + u8 mipi_lanes; + + int (*s_power)(int enable); + struct regulator_bulk_data supplies[S5K6AA_NUM_SUPPLIES]; + struct s5k6aa_gpio gpio[GPIO_NUM]; + + /* external master clock frequency */ + unsigned long mclk_frequency; + /* ISP internal master clock frequency */ + u16 clk_fop; + /* output pixel clock frequency range */ + u16 pclk_fmin; + u16 pclk_fmax; + + unsigned int inv_hflip:1; + unsigned int inv_vflip:1; + + /* protects the struct members below */ + struct mutex lock; + + /* sensor matrix scan window */ + struct v4l2_rect ccd_rect; + + struct s5k6aa_ctrls ctrls; + struct s5k6aa_preset presets[S5K6AA_MAX_PRESETS]; + struct s5k6aa_preset *preset; + const struct s5k6aa_interval *fiv; + + unsigned int streaming:1; + unsigned int apply_cfg:1; + unsigned int apply_crop:1; + unsigned int power; +}; + +static struct s5k6aa_regval s5k6aa_analog_config[] = { + /* Analog settings */ + { 0x112a, 0x0000 }, { 0x1132, 0x0000 }, + { 0x113e, 0x0000 }, { 0x115c, 0x0000 }, + { 0x1164, 0x0000 }, { 0x1174, 0x0000 }, + { 0x1178, 0x0000 }, { 0x077a, 0x0000 }, + { 0x077c, 0x0000 }, { 0x077e, 0x0000 }, + { 0x0780, 0x0000 }, { 0x0782, 0x0000 }, + { 0x0784, 0x0000 }, { 0x0786, 0x0000 }, + { 0x0788, 0x0000 }, { 0x07a2, 0x0000 }, + { 0x07a4, 0x0000 }, { 0x07a6, 0x0000 }, + { 0x07a8, 0x0000 }, { 0x07b6, 0x0000 }, + { 0x07b8, 0x0002 }, { 0x07ba, 0x0004 }, + { 0x07bc, 0x0004 }, { 0x07be, 0x0005 }, + { 0x07c0, 0x0005 }, { S5K6AA_TERM, 0 }, +}; + +/* TODO: Add RGB888 and Bayer format */ +static const struct s5k6aa_pixfmt s5k6aa_formats[] = { + { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG, 5 }, + /* range 16-240 */ + { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_REC709, 6 }, + { V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_JPEG, 0 }, +}; + +static const struct s5k6aa_interval s5k6aa_intervals[] = { + { 1000, {10000, 1000000}, {1280, 1024} }, /* 10 fps */ + { 666, {15000, 1000000}, {1280, 1024} }, /* 15 fps */ + { 500, {20000, 1000000}, {1280, 720} }, /* 20 fps */ + { 400, {25000, 1000000}, {640, 480} }, /* 25 fps */ + { 333, {33300, 1000000}, {640, 480} }, /* 30 fps */ +}; + +#define S5K6AA_INTERVAL_DEF_INDEX 1 /* 15 fps */ + +static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct s5k6aa, ctrls.handler)->sd; +} + +static inline struct s5k6aa *to_s5k6aa(struct v4l2_subdev *sd) +{ + return container_of(sd, struct s5k6aa, sd); +} + +/* Set initial values for all preview presets */ +static void s5k6aa_presets_data_init(struct s5k6aa *s5k6aa) +{ + struct s5k6aa_preset *preset = &s5k6aa->presets[0]; + int i; + + for (i = 0; i < S5K6AA_MAX_PRESETS; i++) { + preset->mbus_fmt.width = S5K6AA_OUT_WIDTH_DEF; + preset->mbus_fmt.height = S5K6AA_OUT_HEIGHT_DEF; + preset->mbus_fmt.code = s5k6aa_formats[0].code; + preset->index = i; + preset->clk_id = 0; + preset++; + } + + s5k6aa->fiv = &s5k6aa_intervals[S5K6AA_INTERVAL_DEF_INDEX]; + s5k6aa->preset = &s5k6aa->presets[0]; +} + +static int s5k6aa_i2c_read(struct i2c_client *client, u16 addr, u16 *val) +{ + u8 wbuf[2] = {addr >> 8, addr & 0xFF}; + struct i2c_msg msg[2]; + u8 rbuf[2]; + int ret; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 2; + msg[0].buf = wbuf; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 2; + msg[1].buf = rbuf; + + ret = i2c_transfer(client->adapter, msg, 2); + *val = be16_to_cpu(*((u16 *)rbuf)); + + v4l2_dbg(3, debug, client, "i2c_read: 0x%04X : 0x%04x\n", addr, *val); + + return ret == 2 ? 0 : ret; +} + +static int s5k6aa_i2c_write(struct i2c_client *client, u16 addr, u16 val) +{ + u8 buf[4] = {addr >> 8, addr & 0xFF, val >> 8, val & 0xFF}; + + int ret = i2c_master_send(client, buf, 4); + v4l2_dbg(3, debug, client, "i2c_write: 0x%04X : 0x%04x\n", addr, val); + + return ret == 4 ? 0 : ret; +} + +/* The command register write, assumes Command_Wr_addH = 0x7000. */ +static int s5k6aa_write(struct i2c_client *c, u16 addr, u16 val) +{ + int ret = s5k6aa_i2c_write(c, REG_CMDWR_ADDRL, addr); + if (ret) + return ret; + return s5k6aa_i2c_write(c, REG_CMDBUF0_ADDR, val); +} + +/* The command register read, assumes Command_Rd_addH = 0x7000. */ +static int s5k6aa_read(struct i2c_client *client, u16 addr, u16 *val) +{ + int ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRL, addr); + if (ret) + return ret; + return s5k6aa_i2c_read(client, REG_CMDBUF0_ADDR, val); +} + +static int s5k6aa_write_array(struct v4l2_subdev *sd, + const struct s5k6aa_regval *msg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u16 addr_incr = 0; + int ret = 0; + + while (msg->addr != S5K6AA_TERM) { + if (addr_incr != 2) + ret = s5k6aa_i2c_write(client, REG_CMDWR_ADDRL, + msg->addr); + if (ret) + break; + ret = s5k6aa_i2c_write(client, REG_CMDBUF0_ADDR, msg->val); + if (ret) + break; + /* Assume that msg->addr is always less than 0xfffc */ + addr_incr = (msg + 1)->addr - msg->addr; + msg++; + } + + return ret; +} + +/* Configure the AHB high address bytes for GTG registers access */ +static int s5k6aa_set_ahb_address(struct i2c_client *client) +{ + int ret = s5k6aa_i2c_write(client, AHB_MSB_ADDR_PTR, GEN_REG_OFFSH); + if (ret) + return ret; + ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRH, HOST_SWIF_OFFSH); + if (ret) + return ret; + return s5k6aa_i2c_write(client, REG_CMDWR_ADDRH, HOST_SWIF_OFFSH); +} + +/** + * s5k6aa_configure_pixel_clock - apply ISP main clock/PLL configuration + * + * Configure the internal ISP PLL for the required output frequency. + * Locking: called with s5k6aa.lock mutex held. + */ +static int s5k6aa_configure_pixel_clocks(struct s5k6aa *s5k6aa) +{ + struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); + unsigned long fmclk = s5k6aa->mclk_frequency / 1000; + u16 status; + int ret; + + if (WARN(fmclk < MIN_MCLK_FREQ_KHZ || fmclk > MAX_MCLK_FREQ_KHZ, + "Invalid clock frequency: %ld\n", fmclk)) + return -EINVAL; + + s5k6aa->pclk_fmin = PCLK_FREQ_MIN; + s5k6aa->pclk_fmax = PCLK_FREQ_MAX; + s5k6aa->clk_fop = SYS_PLL_OUT_FREQ; + + /* External input clock frequency in kHz */ + ret = s5k6aa_write(c, REG_I_INCLK_FREQ_H, fmclk >> 16); + if (!ret) + ret = s5k6aa_write(c, REG_I_INCLK_FREQ_L, fmclk & 0xFFFF); + if (!ret) + ret = s5k6aa_write(c, REG_I_USE_NPVI_CLOCKS, 1); + /* Internal PLL frequency */ + if (!ret) + ret = s5k6aa_write(c, REG_I_OPCLK_4KHZ(0), s5k6aa->clk_fop); + if (!ret) + ret = s5k6aa_write(c, REG_I_MIN_OUTRATE_4KHZ(0), + s5k6aa->pclk_fmin); + if (!ret) + ret = s5k6aa_write(c, REG_I_MAX_OUTRATE_4KHZ(0), + s5k6aa->pclk_fmax); + if (!ret) + ret = s5k6aa_write(c, REG_I_INIT_PARAMS_UPDATED, 1); + if (!ret) + ret = s5k6aa_read(c, REG_I_ERROR_INFO, &status); + + return ret ? ret : (status ? -EINVAL : 0); +} + +/* Set horizontal and vertical image flipping */ +static int s5k6aa_set_mirror(struct s5k6aa *s5k6aa, int horiz_flip) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + int index = s5k6aa->preset->index; + + unsigned int vflip = s5k6aa->ctrls.vflip->val ^ s5k6aa->inv_vflip; + unsigned int flip = (horiz_flip ^ s5k6aa->inv_hflip) | (vflip << 1); + + return s5k6aa_write(client, REG_P_PREV_MIRROR(index), flip); +} + +/* Configure auto/manual white balance and R/G/B gains */ +static int s5k6aa_set_awb(struct s5k6aa *s5k6aa, int awb) +{ + struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); + struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls; + u16 reg; + + int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, ®); + + if (!ret && !awb) { + ret = s5k6aa_write(c, REG_SF_RGAIN, ctrls->gain_red->val); + if (!ret) + ret = s5k6aa_write(c, REG_SF_RGAIN_CHG, 1); + if (ret) + return ret; + + ret = s5k6aa_write(c, REG_SF_GGAIN, ctrls->gain_green->val); + if (!ret) + ret = s5k6aa_write(c, REG_SF_GGAIN_CHG, 1); + if (ret) + return ret; + + ret = s5k6aa_write(c, REG_SF_BGAIN, ctrls->gain_blue->val); + if (!ret) + ret = s5k6aa_write(c, REG_SF_BGAIN_CHG, 1); + } + if (!ret) { + reg = awb ? reg | AALG_WB_EN_MASK : reg & ~AALG_WB_EN_MASK; + ret = s5k6aa_write(c, REG_DBG_AUTOALG_EN, reg); + } + + return ret; +} + +/* Program FW with exposure time, 'exposure' in us units */ +static int s5k6aa_set_user_exposure(struct i2c_client *client, int exposure) +{ + unsigned int time = exposure / 10; + + int ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_L, time & 0xffff); + if (!ret) + ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_H, time >> 16); + if (ret) + return ret; + return s5k6aa_write(client, REG_SF_USR_EXPOSURE_CHG, 1); +} + +static int s5k6aa_set_user_gain(struct i2c_client *client, int gain) +{ + int ret = s5k6aa_write(client, REG_SF_USR_TOT_GAIN, gain); + if (ret) + return ret; + return s5k6aa_write(client, REG_SF_USR_TOT_GAIN_CHG, 1); +} + +/* Set auto/manual exposure and total gain */ +static int s5k6aa_set_auto_exposure(struct s5k6aa *s5k6aa, int value) +{ + struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); + unsigned int exp_time = s5k6aa->ctrls.exposure->val; + u16 auto_alg; + + int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, &auto_alg); + if (ret) + return ret; + + v4l2_dbg(1, debug, c, "man_exp: %d, auto_exp: %d, a_alg: 0x%x\n", + exp_time, value, auto_alg); + + if (value == V4L2_EXPOSURE_AUTO) { + auto_alg |= AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK; + } else { + ret = s5k6aa_set_user_exposure(c, exp_time); + if (ret) + return ret; + ret = s5k6aa_set_user_gain(c, s5k6aa->ctrls.gain->val); + if (ret) + return ret; + auto_alg &= ~(AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK); + } + + return s5k6aa_write(c, REG_DBG_AUTOALG_EN, auto_alg); +} + +static int s5k6aa_set_anti_flicker(struct s5k6aa *s5k6aa, int value) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + u16 auto_alg; + int ret; + + ret = s5k6aa_read(client, REG_DBG_AUTOALG_EN, &auto_alg); + if (ret) + return ret; + + if (value == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) { + auto_alg |= AALG_FLICKER_EN_MASK; + } else { + auto_alg &= ~AALG_FLICKER_EN_MASK; + /* The V4L2_CID_LINE_FREQUENCY control values match + * the register values */ + ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT, value); + if (ret) + return ret; + ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT_CHG, 1); + if (ret) + return ret; + } + + return s5k6aa_write(client, REG_DBG_AUTOALG_EN, auto_alg); +} + +static int s5k6aa_set_colorfx(struct s5k6aa *s5k6aa, int val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + static const struct v4l2_control colorfx[] = { + { V4L2_COLORFX_NONE, 0 }, + { V4L2_COLORFX_BW, 1 }, + { V4L2_COLORFX_NEGATIVE, 2 }, + { V4L2_COLORFX_SEPIA, 3 }, + { V4L2_COLORFX_SKY_BLUE, 4 }, + { V4L2_COLORFX_SKETCH, 5 }, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(colorfx); i++) { + if (colorfx[i].id == val) + return s5k6aa_write(client, REG_G_SPEC_EFFECTS, + colorfx[i].value); + } + return -EINVAL; +} + +static int s5k6aa_preview_config_status(struct i2c_client *client) +{ + u16 error = 0; + int ret = s5k6aa_read(client, REG_G_PREV_CFG_ERROR, &error); + + v4l2_dbg(1, debug, client, "error: 0x%x (%d)\n", error, ret); + return ret ? ret : (error ? -EINVAL : 0); +} + +static int s5k6aa_get_pixfmt_index(struct s5k6aa *s5k6aa, + struct v4l2_mbus_framefmt *mf) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(s5k6aa_formats); i++) + if (mf->colorspace == s5k6aa_formats[i].colorspace && + mf->code == s5k6aa_formats[i].code) + return i; + return 0; +} + +static int s5k6aa_set_output_framefmt(struct s5k6aa *s5k6aa, + struct s5k6aa_preset *preset) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + int fmt_index = s5k6aa_get_pixfmt_index(s5k6aa, &preset->mbus_fmt); + int ret; + + ret = s5k6aa_write(client, REG_P_OUT_WIDTH(preset->index), + preset->mbus_fmt.width); + if (!ret) + ret = s5k6aa_write(client, REG_P_OUT_HEIGHT(preset->index), + preset->mbus_fmt.height); + if (!ret) + ret = s5k6aa_write(client, REG_P_FMT(preset->index), + s5k6aa_formats[fmt_index].reg_p_fmt); + return ret; +} + +static int s5k6aa_set_input_params(struct s5k6aa *s5k6aa) +{ + struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); + struct v4l2_rect *r = &s5k6aa->ccd_rect; + int ret; + + ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_WIDTH, r->width); + if (!ret) + ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_HEIGHT, r->height); + if (!ret) + ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_XOFFS, r->left); + if (!ret) + ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_YOFFS, r->top); + if (!ret) + ret = s5k6aa_write(c, REG_G_INPUTS_CHANGE_REQ, 1); + if (!ret) + s5k6aa->apply_crop = 0; + + return ret; +} + +/** + * s5k6aa_configure_video_bus - configure the video output interface + * @bus_type: video bus type: parallel or MIPI-CSI + * @nlanes: number of MIPI lanes to be used (MIPI-CSI only) + * + * Note: Only parallel bus operation has been tested. + */ +static int s5k6aa_configure_video_bus(struct s5k6aa *s5k6aa, + enum v4l2_mbus_type bus_type, int nlanes) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + u16 cfg = 0; + int ret; + + /* + * TODO: The sensor is supposed to support BT.601 and BT.656 + * but there is nothing indicating how to switch between both + * in the datasheet. For now default BT.601 interface is assumed. + */ + if (bus_type == V4L2_MBUS_CSI2) + cfg = nlanes; + else if (bus_type != V4L2_MBUS_PARALLEL) + return -EINVAL; + + ret = s5k6aa_write(client, REG_OIF_EN_MIPI_LANES, cfg); + if (ret) + return ret; + return s5k6aa_write(client, REG_OIF_CFG_CHG, 1); +} + +/* This function should be called when switching to new user configuration set*/ +static int s5k6aa_new_config_sync(struct i2c_client *client, int timeout, + int cid) +{ + unsigned long end = jiffies + msecs_to_jiffies(timeout); + u16 reg = 1; + int ret; + + ret = s5k6aa_write(client, REG_G_ACTIVE_PREV_CFG, cid); + if (!ret) + ret = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1); + if (!ret) + ret = s5k6aa_write(client, REG_G_NEW_CFG_SYNC, 1); + if (timeout == 0) + return ret; + + while (ret >= 0 && time_is_after_jiffies(end)) { + ret = s5k6aa_read(client, REG_G_NEW_CFG_SYNC, ®); + if (!reg) + return 0; + usleep_range(1000, 5000); + } + return ret ? ret : -ETIMEDOUT; +} + +/** + * s5k6aa_set_prev_config - write user preview register set + * + * Configure output resolution and color fromat, pixel clock + * frequency range, device frame rate type and frame period range. + */ +static int s5k6aa_set_prev_config(struct s5k6aa *s5k6aa, + struct s5k6aa_preset *preset) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + int idx = preset->index; + u16 frame_rate_q; + int ret; + + if (s5k6aa->fiv->reg_fr_time >= S5K6AA_MAX_HIGHRES_FR_TIME) + frame_rate_q = FR_RATE_Q_BEST_FRRATE; + else + frame_rate_q = FR_RATE_Q_BEST_QUALITY; + + ret = s5k6aa_set_output_framefmt(s5k6aa, preset); + if (!ret) + ret = s5k6aa_write(client, REG_P_MAX_OUT_RATE(idx), + s5k6aa->pclk_fmax); + if (!ret) + ret = s5k6aa_write(client, REG_P_MIN_OUT_RATE(idx), + s5k6aa->pclk_fmin); + if (!ret) + ret = s5k6aa_write(client, REG_P_CLK_INDEX(idx), + preset->clk_id); + if (!ret) + ret = s5k6aa_write(client, REG_P_FR_RATE_TYPE(idx), + FR_RATE_DYNAMIC); + if (!ret) + ret = s5k6aa_write(client, REG_P_FR_RATE_Q_TYPE(idx), + frame_rate_q); + if (!ret) + ret = s5k6aa_write(client, REG_P_MAX_FR_TIME(idx), + s5k6aa->fiv->reg_fr_time + 33); + if (!ret) + ret = s5k6aa_write(client, REG_P_MIN_FR_TIME(idx), + s5k6aa->fiv->reg_fr_time - 33); + if (!ret) + ret = s5k6aa_new_config_sync(client, 250, idx); + if (!ret) + ret = s5k6aa_preview_config_status(client); + if (!ret) + s5k6aa->apply_cfg = 0; + + v4l2_dbg(1, debug, client, "Frame interval: %d +/- 3.3ms. (%d)\n", + s5k6aa->fiv->reg_fr_time, ret); + return ret; +} + +/** + * s5k6aa_initialize_isp - basic ISP MCU initialization + * + * Configure AHB addresses for registers read/write; configure PLLs for + * required output pixel clock. The ISP power supply needs to be already + * enabled, with an optional H/W reset. + * Locking: called with s5k6aa.lock mutex held. + */ +static int s5k6aa_initialize_isp(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + int ret; + + s5k6aa->apply_crop = 1; + s5k6aa->apply_cfg = 1; + msleep(100); + + ret = s5k6aa_set_ahb_address(client); + if (ret) + return ret; + ret = s5k6aa_configure_video_bus(s5k6aa, s5k6aa->bus_type, + s5k6aa->mipi_lanes); + if (ret) + return ret; + ret = s5k6aa_write_array(sd, s5k6aa_analog_config); + if (ret) + return ret; + msleep(20); + + return s5k6aa_configure_pixel_clocks(s5k6aa); +} + +static int s5k6aa_gpio_set_value(struct s5k6aa *priv, int id, u32 val) +{ + if (!gpio_is_valid(priv->gpio[id].gpio)) + return 0; + gpio_set_value(priv->gpio[id].gpio, !!val); + return 1; +} + +static int s5k6aa_gpio_assert(struct s5k6aa *priv, int id) +{ + return s5k6aa_gpio_set_value(priv, id, priv->gpio[id].level); +} + +static int s5k6aa_gpio_deassert(struct s5k6aa *priv, int id) +{ + return s5k6aa_gpio_set_value(priv, id, !priv->gpio[id].level); +} + +static int __s5k6aa_power_on(struct s5k6aa *s5k6aa) +{ + int ret; + + ret = regulator_bulk_enable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); + if (ret) + return ret; + if (s5k6aa_gpio_deassert(s5k6aa, STBY)) + usleep_range(150, 200); + + if (s5k6aa->s_power) + ret = s5k6aa->s_power(1); + usleep_range(4000, 4000); + + if (s5k6aa_gpio_deassert(s5k6aa, RST)) + msleep(20); + + return ret; +} + +static int __s5k6aa_power_off(struct s5k6aa *s5k6aa) +{ + int ret; + + if (s5k6aa_gpio_assert(s5k6aa, RST)) + usleep_range(100, 150); + + if (s5k6aa->s_power) { + ret = s5k6aa->s_power(0); + if (ret) + return ret; + } + if (s5k6aa_gpio_assert(s5k6aa, STBY)) + usleep_range(50, 100); + s5k6aa->streaming = 0; + + return regulator_bulk_disable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); +} + +/* + * V4L2 subdev core and video operations + */ +static int s5k6aa_set_power(struct v4l2_subdev *sd, int on) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + int ret = 0; + + mutex_lock(&s5k6aa->lock); + + if (!on == s5k6aa->power) { + if (on) { + ret = __s5k6aa_power_on(s5k6aa); + if (!ret) + ret = s5k6aa_initialize_isp(sd); + } else { + ret = __s5k6aa_power_off(s5k6aa); + } + + if (!ret) + s5k6aa->power += on ? 1 : -1; + } + + mutex_unlock(&s5k6aa->lock); + + if (!on || ret || s5k6aa->power != 1) + return ret; + + return v4l2_ctrl_handler_setup(sd->ctrl_handler); +} + +static int __s5k6aa_stream(struct s5k6aa *s5k6aa, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + int ret = 0; + + ret = s5k6aa_write(client, REG_G_ENABLE_PREV, enable); + if (!ret) + ret = s5k6aa_write(client, REG_G_ENABLE_PREV_CHG, 1); + if (!ret) + s5k6aa->streaming = enable; + + return ret; +} + +static int s5k6aa_s_stream(struct v4l2_subdev *sd, int on) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + int ret = 0; + + mutex_lock(&s5k6aa->lock); + + if (s5k6aa->streaming == !on) { + if (!ret && s5k6aa->apply_cfg) + ret = s5k6aa_set_prev_config(s5k6aa, s5k6aa->preset); + if (s5k6aa->apply_crop) + ret = s5k6aa_set_input_params(s5k6aa); + if (!ret) + ret = __s5k6aa_stream(s5k6aa, !!on); + } + mutex_unlock(&s5k6aa->lock); + + return ret; +} + +static int s5k6aa_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + + mutex_lock(&s5k6aa->lock); + fi->interval = s5k6aa->fiv->interval; + mutex_unlock(&s5k6aa->lock); + + return 0; +} + +static int __s5k6aa_set_frame_interval(struct s5k6aa *s5k6aa, + struct v4l2_subdev_frame_interval *fi) +{ + struct v4l2_mbus_framefmt *mbus_fmt = &s5k6aa->preset->mbus_fmt; + const struct s5k6aa_interval *fiv = &s5k6aa_intervals[0]; + unsigned int err, min_err = UINT_MAX; + unsigned int i, fr_time; + + if (fi->interval.denominator == 0) + return -EINVAL; + + fr_time = fi->interval.numerator * 10000 / fi->interval.denominator; + + for (i = 0; i < ARRAY_SIZE(s5k6aa_intervals); i++) { + const struct s5k6aa_interval *iv = &s5k6aa_intervals[i]; + + if (mbus_fmt->width > iv->size.width || + mbus_fmt->height > iv->size.height) + continue; + + err = abs(iv->reg_fr_time - fr_time); + if (err < min_err) { + fiv = iv; + min_err = err; + } + } + s5k6aa->fiv = fiv; + + v4l2_dbg(1, debug, &s5k6aa->sd, "Changed frame interval to %d us\n", + fiv->reg_fr_time * 100); + return 0; +} + +static int s5k6aa_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + int ret; + + v4l2_dbg(1, debug, sd, "Setting %d/%d frame interval\n", + fi->interval.numerator, fi->interval.denominator); + + mutex_lock(&s5k6aa->lock); + ret = __s5k6aa_set_frame_interval(s5k6aa, fi); + s5k6aa->apply_cfg = 1; + + mutex_unlock(&s5k6aa->lock); + return ret; +} + +/* + * V4L2 subdev pad level and video operations + */ +static int s5k6aa_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + const struct s5k6aa_interval *fi; + int ret = 0; + + if (fie->index > ARRAY_SIZE(s5k6aa_intervals)) + return -EINVAL; + + v4l_bound_align_image(&fie->width, S5K6AA_WIN_WIDTH_MIN, + S5K6AA_WIN_WIDTH_MAX, 1, + &fie->height, S5K6AA_WIN_HEIGHT_MIN, + S5K6AA_WIN_HEIGHT_MAX, 1, 0); + + mutex_lock(&s5k6aa->lock); + fi = &s5k6aa_intervals[fie->index]; + if (fie->width > fi->size.width || fie->height > fi->size.height) + ret = -EINVAL; + else + fie->interval = fi->interval; + mutex_unlock(&s5k6aa->lock); + + return ret; +} + +static int s5k6aa_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(s5k6aa_formats)) + return -EINVAL; + + code->code = s5k6aa_formats[code->index].code; + return 0; +} + +static int s5k6aa_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + int i = ARRAY_SIZE(s5k6aa_formats); + + if (fse->index > 0) + return -EINVAL; + + while (--i) + if (fse->code == s5k6aa_formats[i].code) + break; + + fse->code = s5k6aa_formats[i].code; + fse->min_width = S5K6AA_WIN_WIDTH_MIN; + fse->max_width = S5K6AA_WIN_WIDTH_MAX; + fse->max_height = S5K6AA_WIN_HEIGHT_MIN; + fse->min_height = S5K6AA_WIN_HEIGHT_MAX; + + return 0; +} + +static struct v4l2_rect * +__s5k6aa_get_crop_rect(struct s5k6aa *s5k6aa, struct v4l2_subdev_fh *fh, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) + return &s5k6aa->ccd_rect; + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_crop(fh, 0); + + return NULL; +} + +static void s5k6aa_try_format(struct s5k6aa *s5k6aa, + struct v4l2_mbus_framefmt *mf) +{ + unsigned int index; + + v4l_bound_align_image(&mf->width, S5K6AA_WIN_WIDTH_MIN, + S5K6AA_WIN_WIDTH_MAX, 1, + &mf->height, S5K6AA_WIN_HEIGHT_MIN, + S5K6AA_WIN_HEIGHT_MAX, 1, 0); + + if (mf->colorspace != V4L2_COLORSPACE_JPEG && + mf->colorspace != V4L2_COLORSPACE_REC709) + mf->colorspace = V4L2_COLORSPACE_JPEG; + + index = s5k6aa_get_pixfmt_index(s5k6aa, mf); + + mf->colorspace = s5k6aa_formats[index].colorspace; + mf->code = s5k6aa_formats[index].code; + mf->field = V4L2_FIELD_NONE; +} + +static int s5k6aa_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + struct v4l2_mbus_framefmt *mf; + + memset(fmt->reserved, 0, sizeof(fmt->reserved)); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(fh, 0); + fmt->format = *mf; + return 0; + } + + mutex_lock(&s5k6aa->lock); + fmt->format = s5k6aa->preset->mbus_fmt; + mutex_unlock(&s5k6aa->lock); + + return 0; +} + +static int s5k6aa_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + struct s5k6aa_preset *preset = s5k6aa->preset; + struct v4l2_mbus_framefmt *mf; + struct v4l2_rect *crop; + int ret = 0; + + mutex_lock(&s5k6aa->lock); + s5k6aa_try_format(s5k6aa, &fmt->format); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(fh, fmt->pad); + crop = v4l2_subdev_get_try_crop(fh, 0); + } else { + if (s5k6aa->streaming) { + ret = -EBUSY; + } else { + mf = &preset->mbus_fmt; + crop = &s5k6aa->ccd_rect; + s5k6aa->apply_cfg = 1; + } + } + + if (ret == 0) { + struct v4l2_subdev_frame_interval fiv = { + .interval = {0, 1} + }; + + *mf = fmt->format; + /* + * Make sure the crop window is valid, i.e. its size is + * greater than the output window, as the ISP supports + * only down-scaling. + */ + crop->width = clamp_t(unsigned int, crop->width, mf->width, + S5K6AA_WIN_WIDTH_MAX); + crop->height = clamp_t(unsigned int, crop->height, mf->height, + S5K6AA_WIN_HEIGHT_MAX); + crop->left = clamp_t(unsigned int, crop->left, 0, + S5K6AA_WIN_WIDTH_MAX - crop->width); + crop->top = clamp_t(unsigned int, crop->top, 0, + S5K6AA_WIN_HEIGHT_MAX - crop->height); + + /* Reset to minimum possible frame interval */ + ret = __s5k6aa_set_frame_interval(s5k6aa, &fiv); + } + mutex_unlock(&s5k6aa->lock); + + return ret; +} + +static int s5k6aa_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + struct v4l2_rect *rect; + + memset(crop->reserved, 0, sizeof(crop->reserved)); + mutex_lock(&s5k6aa->lock); + + rect = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which); + if (rect) + crop->rect = *rect; + + mutex_unlock(&s5k6aa->lock); + + v4l2_dbg(1, debug, sd, "Current crop rectangle: (%d,%d)/%dx%d\n", + rect->left, rect->top, rect->width, rect->height); + + return 0; +} + +static int s5k6aa_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + struct v4l2_mbus_framefmt *mf; + unsigned int max_x, max_y; + struct v4l2_rect *crop_r; + + mutex_lock(&s5k6aa->lock); + crop_r = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which); + + if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + mf = &s5k6aa->preset->mbus_fmt; + s5k6aa->apply_crop = 1; + } else { + mf = v4l2_subdev_get_try_format(fh, 0); + } + v4l_bound_align_image(&crop->rect.width, mf->width, + S5K6AA_WIN_WIDTH_MAX, 1, + &crop->rect.height, mf->height, + S5K6AA_WIN_HEIGHT_MAX, 1, 0); + + max_x = (S5K6AA_WIN_WIDTH_MAX - crop->rect.width) & ~1; + max_y = (S5K6AA_WIN_HEIGHT_MAX - crop->rect.height) & ~1; + + crop->rect.left = clamp_t(unsigned int, crop->rect.left, 0, max_x); + crop->rect.top = clamp_t(unsigned int, crop->rect.top, 0, max_y); + + *crop_r = crop->rect; + + mutex_unlock(&s5k6aa->lock); + + v4l2_dbg(1, debug, sd, "Set crop rectangle: (%d,%d)/%dx%d\n", + crop_r->left, crop_r->top, crop_r->width, crop_r->height); + + return 0; +} + +static const struct v4l2_subdev_pad_ops s5k6aa_pad_ops = { + .enum_mbus_code = s5k6aa_enum_mbus_code, + .enum_frame_size = s5k6aa_enum_frame_size, + .enum_frame_interval = s5k6aa_enum_frame_interval, + .get_fmt = s5k6aa_get_fmt, + .set_fmt = s5k6aa_set_fmt, + .get_crop = s5k6aa_get_crop, + .set_crop = s5k6aa_set_crop, +}; + +static const struct v4l2_subdev_video_ops s5k6aa_video_ops = { + .g_frame_interval = s5k6aa_g_frame_interval, + .s_frame_interval = s5k6aa_s_frame_interval, + .s_stream = s5k6aa_s_stream, +}; + +/* + * V4L2 subdev controls + */ + +static int s5k6aa_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = ctrl_to_sd(ctrl); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + int idx, err = 0; + + v4l2_dbg(1, debug, sd, "ctrl: 0x%x, value: %d\n", ctrl->id, ctrl->val); + + mutex_lock(&s5k6aa->lock); + /* + * If the device is not powered up by the host driver do + * not apply any controls to H/W at this time. Instead + * the controls will be restored right after power-up. + */ + if (s5k6aa->power == 0) + goto unlock; + idx = s5k6aa->preset->index; + + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + err = s5k6aa_set_awb(s5k6aa, ctrl->val); + break; + + case V4L2_CID_BRIGHTNESS: + err = s5k6aa_write(client, REG_USER_BRIGHTNESS, ctrl->val); + break; + + case V4L2_CID_COLORFX: + err = s5k6aa_set_colorfx(s5k6aa, ctrl->val); + break; + + case V4L2_CID_CONTRAST: + err = s5k6aa_write(client, REG_USER_CONTRAST, ctrl->val); + break; + + case V4L2_CID_EXPOSURE_AUTO: + err = s5k6aa_set_auto_exposure(s5k6aa, ctrl->val); + break; + + case V4L2_CID_HFLIP: + err = s5k6aa_set_mirror(s5k6aa, ctrl->val); + if (err) + break; + err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1); + break; + + case V4L2_CID_POWER_LINE_FREQUENCY: + err = s5k6aa_set_anti_flicker(s5k6aa, ctrl->val); + break; + + case V4L2_CID_SATURATION: + err = s5k6aa_write(client, REG_USER_SATURATION, ctrl->val); + break; + + case V4L2_CID_SHARPNESS: + err = s5k6aa_write(client, REG_USER_SHARPBLUR, ctrl->val); + break; + + case V4L2_CID_WHITE_BALANCE_TEMPERATURE: + err = s5k6aa_write(client, REG_P_COLORTEMP(idx), ctrl->val); + if (err) + break; + err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1); + break; + } +unlock: + mutex_unlock(&s5k6aa->lock); + return err; +} + +static const struct v4l2_ctrl_ops s5k6aa_ctrl_ops = { + .s_ctrl = s5k6aa_s_ctrl, +}; + +static int s5k6aa_log_status(struct v4l2_subdev *sd) +{ + v4l2_ctrl_handler_log_status(sd->ctrl_handler, sd->name); + return 0; +} + +#define V4L2_CID_RED_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1001) +#define V4L2_CID_GREEN_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1002) +#define V4L2_CID_BLUE_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1003) + +static const struct v4l2_ctrl_config s5k6aa_ctrls[] = { + { + .ops = &s5k6aa_ctrl_ops, + .id = V4L2_CID_RED_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Red", + .min = 0, + .max = 256, + .def = 127, + .step = 1, + }, { + .ops = &s5k6aa_ctrl_ops, + .id = V4L2_CID_GREEN_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Green", + .min = 0, + .max = 256, + .def = 127, + .step = 1, + }, { + .ops = &s5k6aa_ctrl_ops, + .id = V4L2_CID_BLUE_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Blue", + .min = 0, + .max = 256, + .def = 127, + .step = 1, + }, +}; + +static int s5k6aa_initialize_ctrls(struct s5k6aa *s5k6aa) +{ + const struct v4l2_ctrl_ops *ops = &s5k6aa_ctrl_ops; + struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls; + struct v4l2_ctrl_handler *hdl = &ctrls->handler; + + int ret = v4l2_ctrl_handler_init(hdl, 16); + if (ret) + return ret; + /* Auto white balance cluster */ + ctrls->awb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, + 0, 1, 1, 1); + ctrls->gain_red = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[0], NULL); + ctrls->gain_green = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[1], NULL); + ctrls->gain_blue = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[2], NULL); + v4l2_ctrl_auto_cluster(4, &ctrls->awb, 0, false); + + ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); + ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_cluster(2, &ctrls->hflip); + + ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_EXPOSURE_AUTO, + V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO); + /* Exposure time: x 1 us */ + ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, + 0, 6000000U, 1, 100000U); + /* Total gain: 256 <=> 1x */ + ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, + 0, 256, 1, 256); + v4l2_ctrl_auto_cluster(3, &ctrls->auto_exp, 0, false); + + v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, + V4L2_CID_POWER_LINE_FREQUENCY_AUTO); + + v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX, + V4L2_COLORFX_SKY_BLUE, ~0x6f, V4L2_COLORFX_NONE); + + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_WHITE_BALANCE_TEMPERATURE, + 0, 256, 1, 0); + + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, -127, 127, 1, 0); + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -127, 127, 1, 0); + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -127, 127, 1, 0); + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, -127, 127, 1, 0); + + if (hdl->error) { + ret = hdl->error; + v4l2_ctrl_handler_free(hdl); + return ret; + } + + s5k6aa->sd.ctrl_handler = hdl; + return 0; +} + +/* + * V4L2 subdev internal operations + */ +static int s5k6aa_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); + struct v4l2_rect *crop = v4l2_subdev_get_try_crop(fh, 0); + + format->colorspace = s5k6aa_formats[0].colorspace; + format->code = s5k6aa_formats[0].code; + format->width = S5K6AA_OUT_WIDTH_DEF; + format->height = S5K6AA_OUT_HEIGHT_DEF; + format->field = V4L2_FIELD_NONE; + + crop->width = S5K6AA_WIN_WIDTH_MAX; + crop->height = S5K6AA_WIN_HEIGHT_MAX; + crop->left = 0; + crop->top = 0; + + return 0; +} + +static int s5k6aa_check_fw_revision(struct s5k6aa *s5k6aa) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + u16 api_ver = 0, fw_rev = 0; + + int ret = s5k6aa_set_ahb_address(client); + + if (!ret) + ret = s5k6aa_read(client, REG_FW_APIVER, &api_ver); + if (!ret) + ret = s5k6aa_read(client, REG_FW_REVISION, &fw_rev); + if (ret) { + v4l2_err(&s5k6aa->sd, "FW revision check failed!\n"); + return ret; + } + + v4l2_info(&s5k6aa->sd, "FW API ver.: 0x%X, FW rev.: 0x%X\n", + api_ver, fw_rev); + + return api_ver == S5K6AAFX_FW_APIVER ? 0 : -ENODEV; +} + +static int s5k6aa_registered(struct v4l2_subdev *sd) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + int ret; + + mutex_lock(&s5k6aa->lock); + ret = __s5k6aa_power_on(s5k6aa); + if (!ret) { + msleep(100); + ret = s5k6aa_check_fw_revision(s5k6aa); + __s5k6aa_power_off(s5k6aa); + } + mutex_unlock(&s5k6aa->lock); + + return ret; +} + +static const struct v4l2_subdev_internal_ops s5k6aa_subdev_internal_ops = { + .registered = s5k6aa_registered, + .open = s5k6aa_open, +}; + +static const struct v4l2_subdev_core_ops s5k6aa_core_ops = { + .s_power = s5k6aa_set_power, + .log_status = s5k6aa_log_status, +}; + +static const struct v4l2_subdev_ops s5k6aa_subdev_ops = { + .core = &s5k6aa_core_ops, + .pad = &s5k6aa_pad_ops, + .video = &s5k6aa_video_ops, +}; + +/* + * GPIO setup + */ +static int s5k6aa_configure_gpio(int nr, int val, const char *name) +{ + unsigned long flags = val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; + int ret; + + if (!gpio_is_valid(nr)) + return 0; + ret = gpio_request_one(nr, flags, name); + if (!ret) + gpio_export(nr, 0); + return ret; +} + +static void s5k6aa_free_gpios(struct s5k6aa *s5k6aa) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(s5k6aa->gpio); i++) { + if (!gpio_is_valid(s5k6aa->gpio[i].gpio)) + continue; + gpio_free(s5k6aa->gpio[i].gpio); + s5k6aa->gpio[i].gpio = -EINVAL; + } +} + +static int s5k6aa_configure_gpios(struct s5k6aa *s5k6aa, + const struct s5k6aa_platform_data *pdata) +{ + const struct s5k6aa_gpio *gpio = &pdata->gpio_stby; + int ret; + + s5k6aa->gpio[STBY].gpio = -EINVAL; + s5k6aa->gpio[RST].gpio = -EINVAL; + + ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_STBY"); + if (ret) { + s5k6aa_free_gpios(s5k6aa); + return ret; + } + s5k6aa->gpio[STBY] = *gpio; + if (gpio_is_valid(gpio->gpio)) + gpio_set_value(gpio->gpio, 0); + + gpio = &pdata->gpio_reset; + ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_RST"); + if (ret) { + s5k6aa_free_gpios(s5k6aa); + return ret; + } + s5k6aa->gpio[RST] = *gpio; + if (gpio_is_valid(gpio->gpio)) + gpio_set_value(gpio->gpio, 0); + + return 0; +} + +static int s5k6aa_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct s5k6aa_platform_data *pdata = client->dev.platform_data; + struct v4l2_subdev *sd; + struct s5k6aa *s5k6aa; + int i, ret; + + if (pdata == NULL) { + dev_err(&client->dev, "Platform data not specified\n"); + return -EINVAL; + } + + if (pdata->mclk_frequency == 0) { + dev_err(&client->dev, "MCLK frequency not specified\n"); + return -EINVAL; + } + + s5k6aa = devm_kzalloc(&client->dev, sizeof(*s5k6aa), GFP_KERNEL); + if (!s5k6aa) + return -ENOMEM; + + mutex_init(&s5k6aa->lock); + + s5k6aa->mclk_frequency = pdata->mclk_frequency; + s5k6aa->bus_type = pdata->bus_type; + s5k6aa->mipi_lanes = pdata->nlanes; + s5k6aa->s_power = pdata->set_power; + s5k6aa->inv_hflip = pdata->horiz_flip; + s5k6aa->inv_vflip = pdata->vert_flip; + + sd = &s5k6aa->sd; + v4l2_i2c_subdev_init(sd, client, &s5k6aa_subdev_ops); + strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name)); + + sd->internal_ops = &s5k6aa_subdev_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + s5k6aa->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; + ret = media_entity_init(&sd->entity, 1, &s5k6aa->pad, 0); + if (ret) + return ret; + + ret = s5k6aa_configure_gpios(s5k6aa, pdata); + if (ret) + goto out_err2; + + for (i = 0; i < S5K6AA_NUM_SUPPLIES; i++) + s5k6aa->supplies[i].supply = s5k6aa_supply_names[i]; + + ret = regulator_bulk_get(&client->dev, S5K6AA_NUM_SUPPLIES, + s5k6aa->supplies); + if (ret) { + dev_err(&client->dev, "Failed to get regulators\n"); + goto out_err3; + } + + ret = s5k6aa_initialize_ctrls(s5k6aa); + if (ret) + goto out_err4; + + s5k6aa_presets_data_init(s5k6aa); + + s5k6aa->ccd_rect.width = S5K6AA_WIN_WIDTH_MAX; + s5k6aa->ccd_rect.height = S5K6AA_WIN_HEIGHT_MAX; + s5k6aa->ccd_rect.left = 0; + s5k6aa->ccd_rect.top = 0; + + return 0; + +out_err4: + regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); +out_err3: + s5k6aa_free_gpios(s5k6aa); +out_err2: + media_entity_cleanup(&s5k6aa->sd.entity); + return ret; +} + +static int s5k6aa_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); + media_entity_cleanup(&sd->entity); + regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); + s5k6aa_free_gpios(s5k6aa); + + return 0; +} + +static const struct i2c_device_id s5k6aa_id[] = { + { DRIVER_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, s5k6aa_id); + + +static struct i2c_driver s5k6aa_i2c_driver = { + .driver = { + .name = DRIVER_NAME + }, + .probe = s5k6aa_probe, + .remove = s5k6aa_remove, + .id_table = s5k6aa_id, +}; + +module_i2c_driver(s5k6aa_i2c_driver); + +MODULE_DESCRIPTION("Samsung S5K6AA(FX) SXGA camera driver"); +MODULE_AUTHOR("Sylwester Nawrocki "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/saa6588.c b/drivers/media/i2c/saa6588.c new file mode 100644 index 00000000000..0caac50d7cf --- /dev/null +++ b/drivers/media/i2c/saa6588.c @@ -0,0 +1,542 @@ +/* + Driver for SAA6588 RDS decoder + + (c) 2005 Hans J. Koch + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +/* insmod options */ +static unsigned int debug; +static unsigned int xtal; +static unsigned int mmbs; +static unsigned int plvl; +static unsigned int bufblocks = 100; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug messages"); +module_param(xtal, int, 0); +MODULE_PARM_DESC(xtal, "select oscillator frequency (0..3), default 0"); +module_param(mmbs, int, 0); +MODULE_PARM_DESC(mmbs, "enable MMBS mode: 0=off (default), 1=on"); +module_param(plvl, int, 0); +MODULE_PARM_DESC(plvl, "select pause level (0..3), default 0"); +module_param(bufblocks, int, 0); +MODULE_PARM_DESC(bufblocks, "number of buffered blocks, default 100"); + +MODULE_DESCRIPTION("v4l2 driver module for SAA6588 RDS decoder"); +MODULE_AUTHOR("Hans J. Koch "); + +MODULE_LICENSE("GPL"); + +/* ---------------------------------------------------------------------- */ + +#define UNSET (-1U) +#define PREFIX "saa6588: " +#define dprintk if (debug) printk + +struct saa6588 { + struct v4l2_subdev sd; + struct delayed_work work; + spinlock_t lock; + unsigned char *buffer; + unsigned int buf_size; + unsigned int rd_index; + unsigned int wr_index; + unsigned int block_count; + unsigned char last_blocknum; + wait_queue_head_t read_queue; + int data_available_for_read; + u8 sync; +}; + +static inline struct saa6588 *to_saa6588(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa6588, sd); +} + +/* ---------------------------------------------------------------------- */ + +/* + * SAA6588 defines + */ + +/* Initialization and mode control byte (0w) */ + +/* bit 0+1 (DAC0/DAC1) */ +#define cModeStandard 0x00 +#define cModeFastPI 0x01 +#define cModeReducedRequest 0x02 +#define cModeInvalid 0x03 + +/* bit 2 (RBDS) */ +#define cProcessingModeRDS 0x00 +#define cProcessingModeRBDS 0x04 + +/* bit 3+4 (SYM0/SYM1) */ +#define cErrCorrectionNone 0x00 +#define cErrCorrection2Bits 0x08 +#define cErrCorrection5Bits 0x10 +#define cErrCorrectionNoneRBDS 0x18 + +/* bit 5 (NWSY) */ +#define cSyncNormal 0x00 +#define cSyncRestart 0x20 + +/* bit 6 (TSQD) */ +#define cSigQualityDetectOFF 0x00 +#define cSigQualityDetectON 0x40 + +/* bit 7 (SQCM) */ +#define cSigQualityTriggered 0x00 +#define cSigQualityContinous 0x80 + +/* Pause level and flywheel control byte (1w) */ + +/* bits 0..5 (FEB0..FEB5) */ +#define cFlywheelMaxBlocksMask 0x3F +#define cFlywheelDefault 0x20 + +/* bits 6+7 (PL0/PL1) */ +#define cPauseLevel_11mV 0x00 +#define cPauseLevel_17mV 0x40 +#define cPauseLevel_27mV 0x80 +#define cPauseLevel_43mV 0xC0 + +/* Pause time/oscillator frequency/quality detector control byte (1w) */ + +/* bits 0..4 (SQS0..SQS4) */ +#define cQualityDetectSensMask 0x1F +#define cQualityDetectDefault 0x0F + +/* bit 5 (SOSC) */ +#define cSelectOscFreqOFF 0x00 +#define cSelectOscFreqON 0x20 + +/* bit 6+7 (PTF0/PTF1) */ +#define cOscFreq_4332kHz 0x00 +#define cOscFreq_8664kHz 0x40 +#define cOscFreq_12996kHz 0x80 +#define cOscFreq_17328kHz 0xC0 + +/* ---------------------------------------------------------------------- */ + +static int block_to_user_buf(struct saa6588 *s, unsigned char __user *user_buf) +{ + int i; + + if (s->rd_index == s->wr_index) { + if (debug > 2) + dprintk(PREFIX "Read: buffer empty.\n"); + return 0; + } + + if (debug > 2) { + dprintk(PREFIX "Read: "); + for (i = s->rd_index; i < s->rd_index + 3; i++) + dprintk("0x%02x ", s->buffer[i]); + } + + if (copy_to_user(user_buf, &s->buffer[s->rd_index], 3)) + return -EFAULT; + + s->rd_index += 3; + if (s->rd_index >= s->buf_size) + s->rd_index = 0; + s->block_count--; + + if (debug > 2) + dprintk("%d blocks total.\n", s->block_count); + + return 1; +} + +static void read_from_buf(struct saa6588 *s, struct saa6588_command *a) +{ + unsigned long flags; + + unsigned char __user *buf_ptr = a->buffer; + unsigned int i; + unsigned int rd_blocks; + + a->result = 0; + if (!a->buffer) + return; + + while (!s->data_available_for_read) { + int ret = wait_event_interruptible(s->read_queue, + s->data_available_for_read); + if (ret == -ERESTARTSYS) { + a->result = -EINTR; + return; + } + } + + spin_lock_irqsave(&s->lock, flags); + rd_blocks = a->block_count; + if (rd_blocks > s->block_count) + rd_blocks = s->block_count; + + if (!rd_blocks) { + spin_unlock_irqrestore(&s->lock, flags); + return; + } + + for (i = 0; i < rd_blocks; i++) { + if (block_to_user_buf(s, buf_ptr)) { + buf_ptr += 3; + a->result++; + } else + break; + } + a->result *= 3; + s->data_available_for_read = (s->block_count > 0); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void block_to_buf(struct saa6588 *s, unsigned char *blockbuf) +{ + unsigned int i; + + if (debug > 3) + dprintk(PREFIX "New block: "); + + for (i = 0; i < 3; ++i) { + if (debug > 3) + dprintk("0x%02x ", blockbuf[i]); + s->buffer[s->wr_index] = blockbuf[i]; + s->wr_index++; + } + + if (s->wr_index >= s->buf_size) + s->wr_index = 0; + + if (s->wr_index == s->rd_index) { + s->rd_index += 3; + if (s->rd_index >= s->buf_size) + s->rd_index = 0; + } else + s->block_count++; + + if (debug > 3) + dprintk("%d blocks total.\n", s->block_count); +} + +static void saa6588_i2c_poll(struct saa6588 *s) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s->sd); + unsigned long flags; + unsigned char tmpbuf[6]; + unsigned char blocknum; + unsigned char tmp; + + /* Although we only need 3 bytes, we have to read at least 6. + SAA6588 returns garbage otherwise. */ + if (6 != i2c_master_recv(client, &tmpbuf[0], 6)) { + if (debug > 1) + dprintk(PREFIX "read error!\n"); + return; + } + + s->sync = tmpbuf[0] & 0x10; + if (!s->sync) + return; + blocknum = tmpbuf[0] >> 5; + if (blocknum == s->last_blocknum) { + if (debug > 3) + dprintk("Saw block %d again.\n", blocknum); + return; + } + + s->last_blocknum = blocknum; + + /* + Byte order according to v4l2 specification: + + Byte 0: Least Significant Byte of RDS Block + Byte 1: Most Significant Byte of RDS Block + Byte 2 Bit 7: Error bit. Indicates that an uncorrectable error + occurred during reception of this block. + Bit 6: Corrected bit. Indicates that an error was + corrected for this data block. + Bits 5-3: Same as bits 0-2. + Bits 2-0: Block number. + + SAA6588 byte order is Status-MSB-LSB, so we have to swap the + first and the last of the 3 bytes block. + */ + + tmp = tmpbuf[2]; + tmpbuf[2] = tmpbuf[0]; + tmpbuf[0] = tmp; + + /* Map 'Invalid block E' to 'Invalid Block' */ + if (blocknum == 6) + blocknum = V4L2_RDS_BLOCK_INVALID; + /* And if are not in mmbs mode, then 'Block E' is also mapped + to 'Invalid Block'. As far as I can tell MMBS is discontinued, + and if there is ever a need to support E blocks, then please + contact the linux-media mailinglist. */ + else if (!mmbs && blocknum == 5) + blocknum = V4L2_RDS_BLOCK_INVALID; + tmp = blocknum; + tmp |= blocknum << 3; /* Received offset == Offset Name (OK ?) */ + if ((tmpbuf[2] & 0x03) == 0x03) + tmp |= V4L2_RDS_BLOCK_ERROR; /* uncorrectable error */ + else if ((tmpbuf[2] & 0x03) != 0x00) + tmp |= V4L2_RDS_BLOCK_CORRECTED; /* corrected error */ + tmpbuf[2] = tmp; /* Is this enough ? Should we also check other bits ? */ + + spin_lock_irqsave(&s->lock, flags); + block_to_buf(s, tmpbuf); + spin_unlock_irqrestore(&s->lock, flags); + s->data_available_for_read = 1; + wake_up_interruptible(&s->read_queue); +} + +static void saa6588_work(struct work_struct *work) +{ + struct saa6588 *s = container_of(work, struct saa6588, work.work); + + saa6588_i2c_poll(s); + schedule_delayed_work(&s->work, msecs_to_jiffies(20)); +} + +static void saa6588_configure(struct saa6588 *s) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s->sd); + unsigned char buf[3]; + int rc; + + buf[0] = cSyncRestart; + if (mmbs) + buf[0] |= cProcessingModeRBDS; + + buf[1] = cFlywheelDefault; + switch (plvl) { + case 0: + buf[1] |= cPauseLevel_11mV; + break; + case 1: + buf[1] |= cPauseLevel_17mV; + break; + case 2: + buf[1] |= cPauseLevel_27mV; + break; + case 3: + buf[1] |= cPauseLevel_43mV; + break; + default: /* nothing */ + break; + } + + buf[2] = cQualityDetectDefault | cSelectOscFreqON; + + switch (xtal) { + case 0: + buf[2] |= cOscFreq_4332kHz; + break; + case 1: + buf[2] |= cOscFreq_8664kHz; + break; + case 2: + buf[2] |= cOscFreq_12996kHz; + break; + case 3: + buf[2] |= cOscFreq_17328kHz; + break; + default: /* nothing */ + break; + } + + dprintk(PREFIX "writing: 0w=0x%02x 1w=0x%02x 2w=0x%02x\n", + buf[0], buf[1], buf[2]); + + rc = i2c_master_send(client, buf, 3); + if (rc != 3) + printk(PREFIX "i2c i/o error: rc == %d (should be 3)\n", rc); +} + +/* ---------------------------------------------------------------------- */ + +static long saa6588_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct saa6588 *s = to_saa6588(sd); + struct saa6588_command *a = arg; + + switch (cmd) { + /* --- open() for /dev/radio --- */ + case SAA6588_CMD_OPEN: + a->result = 0; /* return error if chip doesn't work ??? */ + break; + /* --- close() for /dev/radio --- */ + case SAA6588_CMD_CLOSE: + s->data_available_for_read = 1; + wake_up_interruptible(&s->read_queue); + a->result = 0; + break; + /* --- read() for /dev/radio --- */ + case SAA6588_CMD_READ: + read_from_buf(s, a); + break; + /* --- poll() for /dev/radio --- */ + case SAA6588_CMD_POLL: + a->result = 0; + if (s->data_available_for_read) { + a->result |= POLLIN | POLLRDNORM; + } + poll_wait(a->instance, &s->read_queue, a->event_list); + break; + + default: + /* nothing */ + return -ENOIOCTLCMD; + } + return 0; +} + +static int saa6588_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct saa6588 *s = to_saa6588(sd); + + vt->capability |= V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO; + if (s->sync) + vt->rxsubchans |= V4L2_TUNER_SUB_RDS; + return 0; +} + +static int saa6588_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct saa6588 *s = to_saa6588(sd); + + saa6588_configure(s); + return 0; +} + +static int saa6588_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA6588, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops saa6588_core_ops = { + .g_chip_ident = saa6588_g_chip_ident, + .ioctl = saa6588_ioctl, +}; + +static const struct v4l2_subdev_tuner_ops saa6588_tuner_ops = { + .g_tuner = saa6588_g_tuner, + .s_tuner = saa6588_s_tuner, +}; + +static const struct v4l2_subdev_ops saa6588_ops = { + .core = &saa6588_core_ops, + .tuner = &saa6588_tuner_ops, +}; + +/* ---------------------------------------------------------------------- */ + +static int saa6588_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct saa6588 *s; + struct v4l2_subdev *sd; + + v4l_info(client, "saa6588 found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + s = kzalloc(sizeof(*s), GFP_KERNEL); + if (s == NULL) + return -ENOMEM; + + s->buf_size = bufblocks * 3; + + s->buffer = kmalloc(s->buf_size, GFP_KERNEL); + if (s->buffer == NULL) { + kfree(s); + return -ENOMEM; + } + sd = &s->sd; + v4l2_i2c_subdev_init(sd, client, &saa6588_ops); + spin_lock_init(&s->lock); + s->block_count = 0; + s->wr_index = 0; + s->rd_index = 0; + s->last_blocknum = 0xff; + init_waitqueue_head(&s->read_queue); + s->data_available_for_read = 0; + + saa6588_configure(s); + + /* start polling via eventd */ + INIT_DELAYED_WORK(&s->work, saa6588_work); + schedule_delayed_work(&s->work, 0); + return 0; +} + +static int saa6588_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct saa6588 *s = to_saa6588(sd); + + v4l2_device_unregister_subdev(sd); + + cancel_delayed_work_sync(&s->work); + + kfree(s->buffer); + kfree(s); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id saa6588_id[] = { + { "saa6588", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa6588_id); + +static struct i2c_driver saa6588_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "saa6588", + }, + .probe = saa6588_probe, + .remove = saa6588_remove, + .id_table = saa6588_id, +}; + +module_i2c_driver(saa6588_driver); diff --git a/drivers/media/i2c/saa7110.c b/drivers/media/i2c/saa7110.c new file mode 100644 index 00000000000..51cd4c8f052 --- /dev/null +++ b/drivers/media/i2c/saa7110.c @@ -0,0 +1,494 @@ +/* + * saa7110 - Philips SAA7110(A) video decoder driver + * + * Copyright (C) 1998 Pauline Middelink + * + * Copyright (C) 1999 Wolfgang Scherr + * Copyright (C) 2000 Serguei Miridonov + * - some corrections for Pinnacle Systems Inc. DC10plus card. + * + * Changes by Ronald Bultje + * - moved over to linux>=2.4.x i2c protocol (1/1/2003) + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("Philips SAA7110 video decoder driver"); +MODULE_AUTHOR("Pauline Middelink"); +MODULE_LICENSE("GPL"); + + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +#define SAA7110_MAX_INPUT 9 /* 6 CVBS, 3 SVHS */ +#define SAA7110_MAX_OUTPUT 1 /* 1 YUV */ + +#define SAA7110_NR_REG 0x35 + +struct saa7110 { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + u8 reg[SAA7110_NR_REG]; + + v4l2_std_id norm; + int input; + int enable; + + wait_queue_head_t wq; +}; + +static inline struct saa7110 *to_saa7110(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa7110, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct saa7110, hdl)->sd; +} + +/* ----------------------------------------------------------------------- */ +/* I2C support functions */ +/* ----------------------------------------------------------------------- */ + +static int saa7110_write(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct saa7110 *decoder = to_saa7110(sd); + + decoder->reg[reg] = value; + return i2c_smbus_write_byte_data(client, reg, value); +} + +static int saa7110_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct saa7110 *decoder = to_saa7110(sd); + int ret = -1; + u8 reg = *data; /* first register to write to */ + + /* Sanity check */ + if (reg + (len - 1) > SAA7110_NR_REG) + return ret; + + /* the saa7110 has an autoincrement function, use it if + * the adapter understands raw I2C */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + ret = i2c_master_send(client, data, len); + + /* Cache the written data */ + memcpy(decoder->reg + reg, data + 1, len - 1); + } else { + for (++data, --len; len; len--) { + ret = saa7110_write(sd, reg++, *data++); + if (ret < 0) + break; + } + } + + return ret; +} + +static inline int saa7110_read(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_read_byte(client); +} + +/* ----------------------------------------------------------------------- */ +/* SAA7110 functions */ +/* ----------------------------------------------------------------------- */ + +#define FRESP_06H_COMPST 0x03 /*0x13*/ +#define FRESP_06H_SVIDEO 0x83 /*0xC0*/ + + +static int saa7110_selmux(struct v4l2_subdev *sd, int chan) +{ + static const unsigned char modes[9][8] = { + /* mode 0 */ + {FRESP_06H_COMPST, 0xD9, 0x17, 0x40, 0x03, + 0x44, 0x75, 0x16}, + /* mode 1 */ + {FRESP_06H_COMPST, 0xD8, 0x17, 0x40, 0x03, + 0x44, 0x75, 0x16}, + /* mode 2 */ + {FRESP_06H_COMPST, 0xBA, 0x07, 0x91, 0x03, + 0x60, 0xB5, 0x05}, + /* mode 3 */ + {FRESP_06H_COMPST, 0xB8, 0x07, 0x91, 0x03, + 0x60, 0xB5, 0x05}, + /* mode 4 */ + {FRESP_06H_COMPST, 0x7C, 0x07, 0xD2, 0x83, + 0x60, 0xB5, 0x03}, + /* mode 5 */ + {FRESP_06H_COMPST, 0x78, 0x07, 0xD2, 0x83, + 0x60, 0xB5, 0x03}, + /* mode 6 */ + {FRESP_06H_SVIDEO, 0x59, 0x17, 0x42, 0xA3, + 0x44, 0x75, 0x12}, + /* mode 7 */ + {FRESP_06H_SVIDEO, 0x9A, 0x17, 0xB1, 0x13, + 0x60, 0xB5, 0x14}, + /* mode 8 */ + {FRESP_06H_SVIDEO, 0x3C, 0x27, 0xC1, 0x23, + 0x44, 0x75, 0x21} + }; + struct saa7110 *decoder = to_saa7110(sd); + const unsigned char *ptr = modes[chan]; + + saa7110_write(sd, 0x06, ptr[0]); /* Luminance control */ + saa7110_write(sd, 0x20, ptr[1]); /* Analog Control #1 */ + saa7110_write(sd, 0x21, ptr[2]); /* Analog Control #2 */ + saa7110_write(sd, 0x22, ptr[3]); /* Mixer Control #1 */ + saa7110_write(sd, 0x2C, ptr[4]); /* Mixer Control #2 */ + saa7110_write(sd, 0x30, ptr[5]); /* ADCs gain control */ + saa7110_write(sd, 0x31, ptr[6]); /* Mixer Control #3 */ + saa7110_write(sd, 0x21, ptr[7]); /* Analog Control #2 */ + decoder->input = chan; + + return 0; +} + +static const unsigned char initseq[1 + SAA7110_NR_REG] = { + 0, 0x4C, 0x3C, 0x0D, 0xEF, 0xBD, 0xF2, 0x03, 0x00, + /* 0x08 */ 0xF8, 0xF8, 0x60, 0x60, 0x00, 0x86, 0x18, 0x90, + /* 0x10 */ 0x00, 0x59, 0x40, 0x46, 0x42, 0x1A, 0xFF, 0xDA, + /* 0x18 */ 0xF2, 0x8B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x20 */ 0xD9, 0x16, 0x40, 0x41, 0x80, 0x41, 0x80, 0x4F, + /* 0x28 */ 0xFE, 0x01, 0xCF, 0x0F, 0x03, 0x01, 0x03, 0x0C, + /* 0x30 */ 0x44, 0x71, 0x02, 0x8C, 0x02 +}; + +static v4l2_std_id determine_norm(struct v4l2_subdev *sd) +{ + DEFINE_WAIT(wait); + struct saa7110 *decoder = to_saa7110(sd); + int status; + + /* mode changed, start automatic detection */ + saa7110_write_block(sd, initseq, sizeof(initseq)); + saa7110_selmux(sd, decoder->input); + prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(250)); + finish_wait(&decoder->wq, &wait); + status = saa7110_read(sd); + if (status & 0x40) { + v4l2_dbg(1, debug, sd, "status=0x%02x (no signal)\n", status); + return decoder->norm; /* no change*/ + } + if ((status & 3) == 0) { + saa7110_write(sd, 0x06, 0x83); + if (status & 0x20) { + v4l2_dbg(1, debug, sd, "status=0x%02x (NTSC/no color)\n", status); + /*saa7110_write(sd,0x2E,0x81);*/ + return V4L2_STD_NTSC; + } + v4l2_dbg(1, debug, sd, "status=0x%02x (PAL/no color)\n", status); + /*saa7110_write(sd,0x2E,0x9A);*/ + return V4L2_STD_PAL; + } + /*saa7110_write(sd,0x06,0x03);*/ + if (status & 0x20) { /* 60Hz */ + v4l2_dbg(1, debug, sd, "status=0x%02x (NTSC)\n", status); + saa7110_write(sd, 0x0D, 0x86); + saa7110_write(sd, 0x0F, 0x50); + saa7110_write(sd, 0x11, 0x2C); + /*saa7110_write(sd,0x2E,0x81);*/ + return V4L2_STD_NTSC; + } + + /* 50Hz -> PAL/SECAM */ + saa7110_write(sd, 0x0D, 0x86); + saa7110_write(sd, 0x0F, 0x10); + saa7110_write(sd, 0x11, 0x59); + /*saa7110_write(sd,0x2E,0x9A);*/ + + prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(250)); + finish_wait(&decoder->wq, &wait); + + status = saa7110_read(sd); + if ((status & 0x03) == 0x01) { + v4l2_dbg(1, debug, sd, "status=0x%02x (SECAM)\n", status); + saa7110_write(sd, 0x0D, 0x87); + return V4L2_STD_SECAM; + } + v4l2_dbg(1, debug, sd, "status=0x%02x (PAL)\n", status); + return V4L2_STD_PAL; +} + +static int saa7110_g_input_status(struct v4l2_subdev *sd, u32 *pstatus) +{ + struct saa7110 *decoder = to_saa7110(sd); + int res = V4L2_IN_ST_NO_SIGNAL; + int status = saa7110_read(sd); + + v4l2_dbg(1, debug, sd, "status=0x%02x norm=%llx\n", + status, (unsigned long long)decoder->norm); + if (!(status & 0x40)) + res = 0; + if (!(status & 0x03)) + res |= V4L2_IN_ST_NO_COLOR; + + *pstatus = res; + return 0; +} + +static int saa7110_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + *(v4l2_std_id *)std = determine_norm(sd); + return 0; +} + +static int saa7110_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct saa7110 *decoder = to_saa7110(sd); + + if (decoder->norm != std) { + decoder->norm = std; + /*saa7110_write(sd, 0x06, 0x03);*/ + if (std & V4L2_STD_NTSC) { + saa7110_write(sd, 0x0D, 0x86); + saa7110_write(sd, 0x0F, 0x50); + saa7110_write(sd, 0x11, 0x2C); + /*saa7110_write(sd, 0x2E, 0x81);*/ + v4l2_dbg(1, debug, sd, "switched to NTSC\n"); + } else if (std & V4L2_STD_PAL) { + saa7110_write(sd, 0x0D, 0x86); + saa7110_write(sd, 0x0F, 0x10); + saa7110_write(sd, 0x11, 0x59); + /*saa7110_write(sd, 0x2E, 0x9A);*/ + v4l2_dbg(1, debug, sd, "switched to PAL\n"); + } else if (std & V4L2_STD_SECAM) { + saa7110_write(sd, 0x0D, 0x87); + saa7110_write(sd, 0x0F, 0x10); + saa7110_write(sd, 0x11, 0x59); + /*saa7110_write(sd, 0x2E, 0x9A);*/ + v4l2_dbg(1, debug, sd, "switched to SECAM\n"); + } else { + return -EINVAL; + } + } + return 0; +} + +static int saa7110_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct saa7110 *decoder = to_saa7110(sd); + + if (input >= SAA7110_MAX_INPUT) { + v4l2_dbg(1, debug, sd, "input=%d not available\n", input); + return -EINVAL; + } + if (decoder->input != input) { + saa7110_selmux(sd, input); + v4l2_dbg(1, debug, sd, "switched to input=%d\n", input); + } + return 0; +} + +static int saa7110_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct saa7110 *decoder = to_saa7110(sd); + + if (decoder->enable != enable) { + decoder->enable = enable; + saa7110_write(sd, 0x0E, enable ? 0x18 : 0x80); + v4l2_dbg(1, debug, sd, "YUV %s\n", enable ? "on" : "off"); + } + return 0; +} + +static int saa7110_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + saa7110_write(sd, 0x19, ctrl->val); + break; + case V4L2_CID_CONTRAST: + saa7110_write(sd, 0x13, ctrl->val); + break; + case V4L2_CID_SATURATION: + saa7110_write(sd, 0x12, ctrl->val); + break; + case V4L2_CID_HUE: + saa7110_write(sd, 0x07, ctrl->val); + break; + default: + return -EINVAL; + } + return 0; +} + +static int saa7110_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7110, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops saa7110_ctrl_ops = { + .s_ctrl = saa7110_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops saa7110_core_ops = { + .g_chip_ident = saa7110_g_chip_ident, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, + .s_std = saa7110_s_std, +}; + +static const struct v4l2_subdev_video_ops saa7110_video_ops = { + .s_routing = saa7110_s_routing, + .s_stream = saa7110_s_stream, + .querystd = saa7110_querystd, + .g_input_status = saa7110_g_input_status, +}; + +static const struct v4l2_subdev_ops saa7110_ops = { + .core = &saa7110_core_ops, + .video = &saa7110_video_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static int saa7110_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct saa7110 *decoder; + struct v4l2_subdev *sd; + int rv; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return -ENODEV; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + decoder = kzalloc(sizeof(struct saa7110), GFP_KERNEL); + if (!decoder) + return -ENOMEM; + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &saa7110_ops); + decoder->norm = V4L2_STD_PAL; + decoder->input = 0; + decoder->enable = 1; + v4l2_ctrl_handler_init(&decoder->hdl, 2); + v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, + V4L2_CID_CONTRAST, 0, 127, 1, 64); + v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, + V4L2_CID_SATURATION, 0, 127, 1, 64); + v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + sd->ctrl_handler = &decoder->hdl; + if (decoder->hdl.error) { + int err = decoder->hdl.error; + + v4l2_ctrl_handler_free(&decoder->hdl); + kfree(decoder); + return err; + } + v4l2_ctrl_handler_setup(&decoder->hdl); + + init_waitqueue_head(&decoder->wq); + + rv = saa7110_write_block(sd, initseq, sizeof(initseq)); + if (rv < 0) { + v4l2_dbg(1, debug, sd, "init status %d\n", rv); + } else { + int ver, status; + saa7110_write(sd, 0x21, 0x10); + saa7110_write(sd, 0x0e, 0x18); + saa7110_write(sd, 0x0D, 0x04); + ver = saa7110_read(sd); + saa7110_write(sd, 0x0D, 0x06); + /*mdelay(150);*/ + status = saa7110_read(sd); + v4l2_dbg(1, debug, sd, "version %x, status=0x%02x\n", + ver, status); + saa7110_write(sd, 0x0D, 0x86); + saa7110_write(sd, 0x0F, 0x10); + saa7110_write(sd, 0x11, 0x59); + /*saa7110_write(sd, 0x2E, 0x9A);*/ + } + + /*saa7110_selmux(sd,0);*/ + /*determine_norm(sd);*/ + /* setup and implicit mode 0 select has been performed */ + + return 0; +} + +static int saa7110_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct saa7110 *decoder = to_saa7110(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&decoder->hdl); + kfree(decoder); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id saa7110_id[] = { + { "saa7110", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa7110_id); + +static struct i2c_driver saa7110_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "saa7110", + }, + .probe = saa7110_probe, + .remove = saa7110_remove, + .id_table = saa7110_id, +}; + +module_i2c_driver(saa7110_driver); diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c new file mode 100644 index 00000000000..2107336cd83 --- /dev/null +++ b/drivers/media/i2c/saa7115.c @@ -0,0 +1,1727 @@ +/* saa711x - Philips SAA711x video decoder driver + * This driver can work with saa7111, saa7111a, saa7113, saa7114, + * saa7115 and saa7118. + * + * Based on saa7114 driver by Maxim Yevtyushkin, which is based on + * the saa7111 driver by Dave Perks. + * + * Copyright (C) 1998 Dave Perks + * Copyright (C) 2002 Maxim Yevtyushkin + * + * Slight changes for video timing and attachment output by + * Wolfgang Scherr + * + * Moved over to the linux >= 2.4.x i2c protocol (1/1/2003) + * by Ronald Bultje + * + * Added saa7115 support by Kevin Thayer + * (2/17/2003) + * + * VBI support (2004) and cleanups (2005) by Hans Verkuil + * + * Copyright (c) 2005-2006 Mauro Carvalho Chehab + * SAA7111, SAA7113 and SAA7118 support + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "saa711x_regs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VRES_60HZ (480+16) + +MODULE_DESCRIPTION("Philips SAA7111/SAA7113/SAA7114/SAA7115/SAA7118 video decoder driver"); +MODULE_AUTHOR( "Maxim Yevtyushkin, Kevin Thayer, Chris Kennedy, " + "Hans Verkuil, Mauro Carvalho Chehab"); +MODULE_LICENSE("GPL"); + +static bool debug; +module_param(debug, bool, 0644); + +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + + +struct saa711x_state { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + + struct { + /* chroma gain control cluster */ + struct v4l2_ctrl *agc; + struct v4l2_ctrl *gain; + }; + + v4l2_std_id std; + int input; + int output; + int enable; + int radio; + int width; + int height; + u32 ident; + u32 audclk_freq; + u32 crystal_freq; + u8 ucgc; + u8 cgcdiv; + u8 apll; +}; + +static inline struct saa711x_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa711x_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct saa711x_state, hdl)->sd; +} + +/* ----------------------------------------------------------------------- */ + +static inline int saa711x_write(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Sanity routine to check if a register is present */ +static int saa711x_has_reg(const int id, const u8 reg) +{ + if (id == V4L2_IDENT_SAA7111) + return reg < 0x20 && reg != 0x01 && reg != 0x0f && + (reg < 0x13 || reg > 0x19) && reg != 0x1d && reg != 0x1e; + if (id == V4L2_IDENT_SAA7111A) + return reg < 0x20 && reg != 0x01 && reg != 0x0f && + reg != 0x14 && reg != 0x18 && reg != 0x19 && + reg != 0x1d && reg != 0x1e; + + /* common for saa7113/4/5/8 */ + if (unlikely((reg >= 0x3b && reg <= 0x3f) || reg == 0x5c || reg == 0x5f || + reg == 0xa3 || reg == 0xa7 || reg == 0xab || reg == 0xaf || (reg >= 0xb5 && reg <= 0xb7) || + reg == 0xd3 || reg == 0xd7 || reg == 0xdb || reg == 0xdf || (reg >= 0xe5 && reg <= 0xe7) || + reg == 0x82 || (reg >= 0x89 && reg <= 0x8e))) + return 0; + + switch (id) { + case V4L2_IDENT_SAA7113: + return reg != 0x14 && (reg < 0x18 || reg > 0x1e) && (reg < 0x20 || reg > 0x3f) && + reg != 0x5d && reg < 0x63; + case V4L2_IDENT_SAA7114: + return (reg < 0x1a || reg > 0x1e) && (reg < 0x20 || reg > 0x2f) && + (reg < 0x63 || reg > 0x7f) && reg != 0x33 && reg != 0x37 && + reg != 0x81 && reg < 0xf0; + case V4L2_IDENT_SAA7115: + return (reg < 0x20 || reg > 0x2f) && reg != 0x65 && (reg < 0xfc || reg > 0xfe); + case V4L2_IDENT_SAA7118: + return (reg < 0x1a || reg > 0x1d) && (reg < 0x20 || reg > 0x22) && + (reg < 0x26 || reg > 0x28) && reg != 0x33 && reg != 0x37 && + (reg < 0x63 || reg > 0x7f) && reg != 0x81 && reg < 0xf0; + } + return 1; +} + +static int saa711x_writeregs(struct v4l2_subdev *sd, const unsigned char *regs) +{ + struct saa711x_state *state = to_state(sd); + unsigned char reg, data; + + while (*regs != 0x00) { + reg = *(regs++); + data = *(regs++); + + /* According with datasheets, reserved regs should be + filled with 0 - seems better not to touch on they */ + if (saa711x_has_reg(state->ident, reg)) { + if (saa711x_write(sd, reg, data) < 0) + return -1; + } else { + v4l2_dbg(1, debug, sd, "tried to access reserved reg 0x%02x\n", reg); + } + } + return 0; +} + +static inline int saa711x_read(struct v4l2_subdev *sd, u8 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_read_byte_data(client, reg); +} + +/* ----------------------------------------------------------------------- */ + +/* SAA7111 initialization table */ +static const unsigned char saa7111_init[] = { + R_01_INC_DELAY, 0x00, /* reserved */ + + /*front end */ + R_02_INPUT_CNTL_1, 0xd0, /* FUSE=3, GUDL=2, MODE=0 */ + R_03_INPUT_CNTL_2, 0x23, /* HLNRS=0, VBSL=1, WPOFF=0, HOLDG=0, + * GAFIX=0, GAI1=256, GAI2=256 */ + R_04_INPUT_CNTL_3, 0x00, /* GAI1=256 */ + R_05_INPUT_CNTL_4, 0x00, /* GAI2=256 */ + + /* decoder */ + R_06_H_SYNC_START, 0xf3, /* HSB at 13(50Hz) / 17(60Hz) + * pixels after end of last line */ + R_07_H_SYNC_STOP, 0xe8, /* HSS seems to be needed to + * work with NTSC, too */ + R_08_SYNC_CNTL, 0xc8, /* AUFD=1, FSEL=1, EXFIL=0, + * VTRC=1, HPLL=0, VNOI=0 */ + R_09_LUMA_CNTL, 0x01, /* BYPS=0, PREF=0, BPSS=0, + * VBLB=0, UPTCV=0, APER=1 */ + R_0A_LUMA_BRIGHT_CNTL, 0x80, + R_0B_LUMA_CONTRAST_CNTL, 0x47, /* 0b - CONT=1.109 */ + R_0C_CHROMA_SAT_CNTL, 0x40, + R_0D_CHROMA_HUE_CNTL, 0x00, + R_0E_CHROMA_CNTL_1, 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0, + * FCTC=0, CHBW=1 */ + R_0F_CHROMA_GAIN_CNTL, 0x00, /* reserved */ + R_10_CHROMA_CNTL_2, 0x48, /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */ + R_11_MODE_DELAY_CNTL, 0x1c, /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1, + * OEYC=1, OEHV=1, VIPB=0, COLO=0 */ + R_12_RT_SIGNAL_CNTL, 0x00, /* 12 - output control 2 */ + R_13_RT_X_PORT_OUT_CNTL, 0x00, /* 13 - output control 3 */ + R_14_ANAL_ADC_COMPAT_CNTL, 0x00, + R_15_VGATE_START_FID_CHG, 0x00, + R_16_VGATE_STOP, 0x00, + R_17_MISC_VGATE_CONF_AND_MSB, 0x00, + + 0x00, 0x00 +}; + +/* SAA7113 init codes */ +static const unsigned char saa7113_init[] = { + R_01_INC_DELAY, 0x08, + R_02_INPUT_CNTL_1, 0xc2, + R_03_INPUT_CNTL_2, 0x30, + R_04_INPUT_CNTL_3, 0x00, + R_05_INPUT_CNTL_4, 0x00, + R_06_H_SYNC_START, 0x89, + R_07_H_SYNC_STOP, 0x0d, + R_08_SYNC_CNTL, 0x88, + R_09_LUMA_CNTL, 0x01, + R_0A_LUMA_BRIGHT_CNTL, 0x80, + R_0B_LUMA_CONTRAST_CNTL, 0x47, + R_0C_CHROMA_SAT_CNTL, 0x40, + R_0D_CHROMA_HUE_CNTL, 0x00, + R_0E_CHROMA_CNTL_1, 0x01, + R_0F_CHROMA_GAIN_CNTL, 0x2a, + R_10_CHROMA_CNTL_2, 0x08, + R_11_MODE_DELAY_CNTL, 0x0c, + R_12_RT_SIGNAL_CNTL, 0x07, + R_13_RT_X_PORT_OUT_CNTL, 0x00, + R_14_ANAL_ADC_COMPAT_CNTL, 0x00, + R_15_VGATE_START_FID_CHG, 0x00, + R_16_VGATE_STOP, 0x00, + R_17_MISC_VGATE_CONF_AND_MSB, 0x00, + + 0x00, 0x00 +}; + +/* If a value differs from the Hauppauge driver values, then the comment starts with + 'was 0xXX' to denote the Hauppauge value. Otherwise the value is identical to what the + Hauppauge driver sets. */ + +/* SAA7114 and SAA7115 initialization table */ +static const unsigned char saa7115_init_auto_input[] = { + /* Front-End Part */ + R_01_INC_DELAY, 0x48, /* white peak control disabled */ + R_03_INPUT_CNTL_2, 0x20, /* was 0x30. 0x20: long vertical blanking */ + R_04_INPUT_CNTL_3, 0x90, /* analog gain set to 0 */ + R_05_INPUT_CNTL_4, 0x90, /* analog gain set to 0 */ + /* Decoder Part */ + R_06_H_SYNC_START, 0xeb, /* horiz sync begin = -21 */ + R_07_H_SYNC_STOP, 0xe0, /* horiz sync stop = -17 */ + R_09_LUMA_CNTL, 0x53, /* 0x53, was 0x56 for 60hz. luminance control */ + R_0A_LUMA_BRIGHT_CNTL, 0x80, /* was 0x88. decoder brightness, 0x80 is itu standard */ + R_0B_LUMA_CONTRAST_CNTL, 0x44, /* was 0x48. decoder contrast, 0x44 is itu standard */ + R_0C_CHROMA_SAT_CNTL, 0x40, /* was 0x47. decoder saturation, 0x40 is itu standard */ + R_0D_CHROMA_HUE_CNTL, 0x00, + R_0F_CHROMA_GAIN_CNTL, 0x00, /* use automatic gain */ + R_10_CHROMA_CNTL_2, 0x06, /* chroma: active adaptive combfilter */ + R_11_MODE_DELAY_CNTL, 0x00, + R_12_RT_SIGNAL_CNTL, 0x9d, /* RTS0 output control: VGATE */ + R_13_RT_X_PORT_OUT_CNTL, 0x80, /* ITU656 standard mode, RTCO output enable RTCE */ + R_14_ANAL_ADC_COMPAT_CNTL, 0x00, + R_18_RAW_DATA_GAIN_CNTL, 0x40, /* gain 0x00 = nominal */ + R_19_RAW_DATA_OFF_CNTL, 0x80, + R_1A_COLOR_KILL_LVL_CNTL, 0x77, /* recommended value */ + R_1B_MISC_TVVCRDET, 0x42, /* recommended value */ + R_1C_ENHAN_COMB_CTRL1, 0xa9, /* recommended value */ + R_1D_ENHAN_COMB_CTRL2, 0x01, /* recommended value */ + + + R_80_GLOBAL_CNTL_1, 0x0, /* No tasks enabled at init */ + + /* Power Device Control */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset device */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* set device programmed, all in operational mode */ + 0x00, 0x00 +}; + +/* Used to reset saa7113, saa7114 and saa7115 */ +static const unsigned char saa7115_cfg_reset_scaler[] = { + R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x00, /* disable I-port output */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */ + R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* enable I-port output */ + 0x00, 0x00 +}; + +/* ============== SAA7715 VIDEO templates ============= */ + +static const unsigned char saa7115_cfg_60hz_video[] = { + R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ + + R_15_VGATE_START_FID_CHG, 0x03, + R_16_VGATE_STOP, 0x11, + R_17_MISC_VGATE_CONF_AND_MSB, 0x9c, + + R_08_SYNC_CNTL, 0x68, /* 0xBO: auto detection, 0x68 = NTSC */ + R_0E_CHROMA_CNTL_1, 0x07, /* video autodetection is on */ + + R_5A_V_OFF_FOR_SLICER, 0x06, /* standard 60hz value for ITU656 line counting */ + + /* Task A */ + R_90_A_TASK_HANDLING_CNTL, 0x80, + R_91_A_X_PORT_FORMATS_AND_CONF, 0x48, + R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL, 0x40, + R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF, 0x84, + + /* hoffset low (input), 0x0002 is minimum */ + R_94_A_HORIZ_INPUT_WINDOW_START, 0x01, + R_95_A_HORIZ_INPUT_WINDOW_START_MSB, 0x00, + + /* hsize low (input), 0x02d0 = 720 */ + R_96_A_HORIZ_INPUT_WINDOW_LENGTH, 0xd0, + R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02, + + R_98_A_VERT_INPUT_WINDOW_START, 0x05, + R_99_A_VERT_INPUT_WINDOW_START_MSB, 0x00, + + R_9A_A_VERT_INPUT_WINDOW_LENGTH, 0x0c, + R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB, 0x00, + + R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH, 0xa0, + R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x05, + + R_9E_A_VERT_OUTPUT_WINDOW_LENGTH, 0x0c, + R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB, 0x00, + + /* Task B */ + R_C0_B_TASK_HANDLING_CNTL, 0x00, + R_C1_B_X_PORT_FORMATS_AND_CONF, 0x08, + R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION, 0x00, + R_C3_B_I_PORT_FORMATS_AND_CONF, 0x80, + + /* 0x0002 is minimum */ + R_C4_B_HORIZ_INPUT_WINDOW_START, 0x02, + R_C5_B_HORIZ_INPUT_WINDOW_START_MSB, 0x00, + + /* 0x02d0 = 720 */ + R_C6_B_HORIZ_INPUT_WINDOW_LENGTH, 0xd0, + R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02, + + /* vwindow start 0x12 = 18 */ + R_C8_B_VERT_INPUT_WINDOW_START, 0x12, + R_C9_B_VERT_INPUT_WINDOW_START_MSB, 0x00, + + /* vwindow length 0xf8 = 248 */ + R_CA_B_VERT_INPUT_WINDOW_LENGTH, VRES_60HZ>>1, + R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB, VRES_60HZ>>9, + + /* hwindow 0x02d0 = 720 */ + R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, 0xd0, + R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x02, + + R_F0_LFCO_PER_LINE, 0xad, /* Set PLL Register. 60hz 525 lines per frame, 27 MHz */ + R_F1_P_I_PARAM_SELECT, 0x05, /* low bit with 0xF0 */ + R_F5_PULSGEN_LINE_LENGTH, 0xad, + R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG, 0x01, + + 0x00, 0x00 +}; + +static const unsigned char saa7115_cfg_50hz_video[] = { + R_80_GLOBAL_CNTL_1, 0x00, + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ + + R_15_VGATE_START_FID_CHG, 0x37, /* VGATE start */ + R_16_VGATE_STOP, 0x16, + R_17_MISC_VGATE_CONF_AND_MSB, 0x99, + + R_08_SYNC_CNTL, 0x28, /* 0x28 = PAL */ + R_0E_CHROMA_CNTL_1, 0x07, + + R_5A_V_OFF_FOR_SLICER, 0x03, /* standard 50hz value */ + + /* Task A */ + R_90_A_TASK_HANDLING_CNTL, 0x81, + R_91_A_X_PORT_FORMATS_AND_CONF, 0x48, + R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL, 0x40, + R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF, 0x84, + + /* This is weird: the datasheet says that you should use 2 as the minimum value, */ + /* but Hauppauge uses 0, and changing that to 2 causes indeed problems (for 50hz) */ + /* hoffset low (input), 0x0002 is minimum */ + R_94_A_HORIZ_INPUT_WINDOW_START, 0x00, + R_95_A_HORIZ_INPUT_WINDOW_START_MSB, 0x00, + + /* hsize low (input), 0x02d0 = 720 */ + R_96_A_HORIZ_INPUT_WINDOW_LENGTH, 0xd0, + R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02, + + R_98_A_VERT_INPUT_WINDOW_START, 0x03, + R_99_A_VERT_INPUT_WINDOW_START_MSB, 0x00, + + /* vsize 0x12 = 18 */ + R_9A_A_VERT_INPUT_WINDOW_LENGTH, 0x12, + R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB, 0x00, + + /* hsize 0x05a0 = 1440 */ + R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH, 0xa0, + R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x05, /* hsize hi (output) */ + R_9E_A_VERT_OUTPUT_WINDOW_LENGTH, 0x12, /* vsize low (output), 0x12 = 18 */ + R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB, 0x00, /* vsize hi (output) */ + + /* Task B */ + R_C0_B_TASK_HANDLING_CNTL, 0x00, + R_C1_B_X_PORT_FORMATS_AND_CONF, 0x08, + R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION, 0x00, + R_C3_B_I_PORT_FORMATS_AND_CONF, 0x80, + + /* This is weird: the datasheet says that you should use 2 as the minimum value, */ + /* but Hauppauge uses 0, and changing that to 2 causes indeed problems (for 50hz) */ + /* hoffset low (input), 0x0002 is minimum. See comment above. */ + R_C4_B_HORIZ_INPUT_WINDOW_START, 0x00, + R_C5_B_HORIZ_INPUT_WINDOW_START_MSB, 0x00, + + /* hsize 0x02d0 = 720 */ + R_C6_B_HORIZ_INPUT_WINDOW_LENGTH, 0xd0, + R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02, + + /* voffset 0x16 = 22 */ + R_C8_B_VERT_INPUT_WINDOW_START, 0x16, + R_C9_B_VERT_INPUT_WINDOW_START_MSB, 0x00, + + /* vsize 0x0120 = 288 */ + R_CA_B_VERT_INPUT_WINDOW_LENGTH, 0x20, + R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB, 0x01, + + /* hsize 0x02d0 = 720 */ + R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, 0xd0, + R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x02, + + R_F0_LFCO_PER_LINE, 0xb0, /* Set PLL Register. 50hz 625 lines per frame, 27 MHz */ + R_F1_P_I_PARAM_SELECT, 0x05, /* low bit with 0xF0, (was 0x05) */ + R_F5_PULSGEN_LINE_LENGTH, 0xb0, + R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG, 0x01, + + 0x00, 0x00 +}; + +/* ============== SAA7715 VIDEO templates (end) ======= */ + +static const unsigned char saa7115_cfg_vbi_on[] = { + R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ + R_80_GLOBAL_CNTL_1, 0x30, /* Activate both tasks */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */ + R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* Enable I-port output */ + + 0x00, 0x00 +}; + +static const unsigned char saa7115_cfg_vbi_off[] = { + R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ + R_80_GLOBAL_CNTL_1, 0x20, /* Activate only task "B" */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */ + R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* Enable I-port output */ + + 0x00, 0x00 +}; + + +static const unsigned char saa7115_init_misc[] = { + R_81_V_SYNC_FLD_ID_SRC_SEL_AND_RETIMED_V_F, 0x01, + R_83_X_PORT_I_O_ENA_AND_OUT_CLK, 0x01, + R_84_I_PORT_SIGNAL_DEF, 0x20, + R_85_I_PORT_SIGNAL_POLAR, 0x21, + R_86_I_PORT_FIFO_FLAG_CNTL_AND_ARBIT, 0xc5, + R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, + + /* Task A */ + R_A0_A_HORIZ_PRESCALING, 0x01, + R_A1_A_ACCUMULATION_LENGTH, 0x00, + R_A2_A_PRESCALER_DC_GAIN_AND_FIR_PREFILTER, 0x00, + + /* Configure controls at nominal value*/ + R_A4_A_LUMA_BRIGHTNESS_CNTL, 0x80, + R_A5_A_LUMA_CONTRAST_CNTL, 0x40, + R_A6_A_CHROMA_SATURATION_CNTL, 0x40, + + /* note: 2 x zoom ensures that VBI lines have same length as video lines. */ + R_A8_A_HORIZ_LUMA_SCALING_INC, 0x00, + R_A9_A_HORIZ_LUMA_SCALING_INC_MSB, 0x02, + + R_AA_A_HORIZ_LUMA_PHASE_OFF, 0x00, + + /* must be horiz lum scaling / 2 */ + R_AC_A_HORIZ_CHROMA_SCALING_INC, 0x00, + R_AD_A_HORIZ_CHROMA_SCALING_INC_MSB, 0x01, + + /* must be offset luma / 2 */ + R_AE_A_HORIZ_CHROMA_PHASE_OFF, 0x00, + + R_B0_A_VERT_LUMA_SCALING_INC, 0x00, + R_B1_A_VERT_LUMA_SCALING_INC_MSB, 0x04, + + R_B2_A_VERT_CHROMA_SCALING_INC, 0x00, + R_B3_A_VERT_CHROMA_SCALING_INC_MSB, 0x04, + + R_B4_A_VERT_SCALING_MODE_CNTL, 0x01, + + R_B8_A_VERT_CHROMA_PHASE_OFF_00, 0x00, + R_B9_A_VERT_CHROMA_PHASE_OFF_01, 0x00, + R_BA_A_VERT_CHROMA_PHASE_OFF_10, 0x00, + R_BB_A_VERT_CHROMA_PHASE_OFF_11, 0x00, + + R_BC_A_VERT_LUMA_PHASE_OFF_00, 0x00, + R_BD_A_VERT_LUMA_PHASE_OFF_01, 0x00, + R_BE_A_VERT_LUMA_PHASE_OFF_10, 0x00, + R_BF_A_VERT_LUMA_PHASE_OFF_11, 0x00, + + /* Task B */ + R_D0_B_HORIZ_PRESCALING, 0x01, + R_D1_B_ACCUMULATION_LENGTH, 0x00, + R_D2_B_PRESCALER_DC_GAIN_AND_FIR_PREFILTER, 0x00, + + /* Configure controls at nominal value*/ + R_D4_B_LUMA_BRIGHTNESS_CNTL, 0x80, + R_D5_B_LUMA_CONTRAST_CNTL, 0x40, + R_D6_B_CHROMA_SATURATION_CNTL, 0x40, + + /* hor lum scaling 0x0400 = 1 */ + R_D8_B_HORIZ_LUMA_SCALING_INC, 0x00, + R_D9_B_HORIZ_LUMA_SCALING_INC_MSB, 0x04, + + R_DA_B_HORIZ_LUMA_PHASE_OFF, 0x00, + + /* must be hor lum scaling / 2 */ + R_DC_B_HORIZ_CHROMA_SCALING, 0x00, + R_DD_B_HORIZ_CHROMA_SCALING_MSB, 0x02, + + /* must be offset luma / 2 */ + R_DE_B_HORIZ_PHASE_OFFSET_CRHOMA, 0x00, + + R_E0_B_VERT_LUMA_SCALING_INC, 0x00, + R_E1_B_VERT_LUMA_SCALING_INC_MSB, 0x04, + + R_E2_B_VERT_CHROMA_SCALING_INC, 0x00, + R_E3_B_VERT_CHROMA_SCALING_INC_MSB, 0x04, + + R_E4_B_VERT_SCALING_MODE_CNTL, 0x01, + + R_E8_B_VERT_CHROMA_PHASE_OFF_00, 0x00, + R_E9_B_VERT_CHROMA_PHASE_OFF_01, 0x00, + R_EA_B_VERT_CHROMA_PHASE_OFF_10, 0x00, + R_EB_B_VERT_CHROMA_PHASE_OFF_11, 0x00, + + R_EC_B_VERT_LUMA_PHASE_OFF_00, 0x00, + R_ED_B_VERT_LUMA_PHASE_OFF_01, 0x00, + R_EE_B_VERT_LUMA_PHASE_OFF_10, 0x00, + R_EF_B_VERT_LUMA_PHASE_OFF_11, 0x00, + + R_F2_NOMINAL_PLL2_DTO, 0x50, /* crystal clock = 24.576 MHz, target = 27MHz */ + R_F3_PLL_INCREMENT, 0x46, + R_F4_PLL2_STATUS, 0x00, + R_F7_PULSE_A_POS_MSB, 0x4b, /* not the recommended settings! */ + R_F8_PULSE_B_POS, 0x00, + R_F9_PULSE_B_POS_MSB, 0x4b, + R_FA_PULSE_C_POS, 0x00, + R_FB_PULSE_C_POS_MSB, 0x4b, + + /* PLL2 lock detection settings: 71 lines 50% phase error */ + R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES, 0x88, + + /* Turn off VBI */ + R_40_SLICER_CNTL_1, 0x20, /* No framing code errors allowed. */ + R_41_LCR_BASE, 0xff, + R_41_LCR_BASE+1, 0xff, + R_41_LCR_BASE+2, 0xff, + R_41_LCR_BASE+3, 0xff, + R_41_LCR_BASE+4, 0xff, + R_41_LCR_BASE+5, 0xff, + R_41_LCR_BASE+6, 0xff, + R_41_LCR_BASE+7, 0xff, + R_41_LCR_BASE+8, 0xff, + R_41_LCR_BASE+9, 0xff, + R_41_LCR_BASE+10, 0xff, + R_41_LCR_BASE+11, 0xff, + R_41_LCR_BASE+12, 0xff, + R_41_LCR_BASE+13, 0xff, + R_41_LCR_BASE+14, 0xff, + R_41_LCR_BASE+15, 0xff, + R_41_LCR_BASE+16, 0xff, + R_41_LCR_BASE+17, 0xff, + R_41_LCR_BASE+18, 0xff, + R_41_LCR_BASE+19, 0xff, + R_41_LCR_BASE+20, 0xff, + R_41_LCR_BASE+21, 0xff, + R_41_LCR_BASE+22, 0xff, + R_58_PROGRAM_FRAMING_CODE, 0x40, + R_59_H_OFF_FOR_SLICER, 0x47, + R_5B_FLD_OFF_AND_MSB_FOR_H_AND_V_OFF, 0x83, + R_5D_DID, 0xbd, + R_5E_SDID, 0x35, + + R_02_INPUT_CNTL_1, 0xc4, /* input tuner -> input 4, amplifier active */ + + R_80_GLOBAL_CNTL_1, 0x20, /* enable task B */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, + 0x00, 0x00 +}; + +static int saa711x_odd_parity(u8 c) +{ + c ^= (c >> 4); + c ^= (c >> 2); + c ^= (c >> 1); + + return c & 1; +} + +static int saa711x_decode_vps(u8 *dst, u8 *p) +{ + static const u8 biphase_tbl[] = { + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, + 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, + 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, + 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, + 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, + 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87, + 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3, + 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85, + 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1, + 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, + 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, + 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, + 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, + 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86, + 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2, + 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84, + 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0, + 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, + 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, + 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, + 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, + 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + }; + int i; + u8 c, err = 0; + + for (i = 0; i < 2 * 13; i += 2) { + err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]]; + c = (biphase_tbl[p[i + 1]] & 0xf) | ((biphase_tbl[p[i]] & 0xf) << 4); + dst[i / 2] = c; + } + return err & 0xf0; +} + +static int saa711x_decode_wss(u8 *p) +{ + static const int wss_bits[8] = { + 0, 0, 0, 1, 0, 1, 1, 1 + }; + unsigned char parity; + int wss = 0; + int i; + + for (i = 0; i < 16; i++) { + int b1 = wss_bits[p[i] & 7]; + int b2 = wss_bits[(p[i] >> 3) & 7]; + + if (b1 == b2) + return -1; + wss |= b2 << i; + } + parity = wss & 15; + parity ^= parity >> 2; + parity ^= parity >> 1; + + if (!(parity & 1)) + return -1; + + return wss; +} + +static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq) +{ + struct saa711x_state *state = to_state(sd); + u32 acpf; + u32 acni; + u32 hz; + u64 f; + u8 acc = 0; /* reg 0x3a, audio clock control */ + + /* Checks for chips that don't have audio clock (saa7111, saa7113) */ + if (!saa711x_has_reg(state->ident, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD)) + return 0; + + v4l2_dbg(1, debug, sd, "set audio clock freq: %d\n", freq); + + /* sanity check */ + if (freq < 32000 || freq > 48000) + return -EINVAL; + + /* hz is the refresh rate times 100 */ + hz = (state->std & V4L2_STD_525_60) ? 5994 : 5000; + /* acpf = (256 * freq) / field_frequency == (256 * 100 * freq) / hz */ + acpf = (25600 * freq) / hz; + /* acni = (256 * freq * 2^23) / crystal_frequency = + (freq * 2^(8+23)) / crystal_frequency = + (freq << 31) / crystal_frequency */ + f = freq; + f = f << 31; + do_div(f, state->crystal_freq); + acni = f; + if (state->ucgc) { + acpf = acpf * state->cgcdiv / 16; + acni = acni * state->cgcdiv / 16; + acc = 0x80; + if (state->cgcdiv == 3) + acc |= 0x40; + } + if (state->apll) + acc |= 0x08; + + saa711x_write(sd, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03); + saa711x_write(sd, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10); + saa711x_write(sd, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc); + + saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff); + saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+1, + (acpf >> 8) & 0xff); + saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+2, + (acpf >> 16) & 0x03); + + saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC, acni & 0xff); + saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+1, (acni >> 8) & 0xff); + saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+2, (acni >> 16) & 0x3f); + state->audclk_freq = freq; + return 0; +} + +static int saa711x_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct saa711x_state *state = to_state(sd); + + switch (ctrl->id) { + case V4L2_CID_CHROMA_AGC: + /* chroma gain cluster */ + if (state->agc->val) + state->gain->val = + saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f; + break; + } + return 0; +} + +static int saa711x_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct saa711x_state *state = to_state(sd); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, ctrl->val); + break; + + case V4L2_CID_CONTRAST: + saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, ctrl->val); + break; + + case V4L2_CID_SATURATION: + saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, ctrl->val); + break; + + case V4L2_CID_HUE: + saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, ctrl->val); + break; + + case V4L2_CID_CHROMA_AGC: + /* chroma gain cluster */ + if (state->agc->val) + saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val); + else + saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val | 0x80); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int saa711x_set_size(struct v4l2_subdev *sd, int width, int height) +{ + struct saa711x_state *state = to_state(sd); + int HPSC, HFSC; + int VSCY; + int res; + int is_50hz = state->std & V4L2_STD_625_50; + int Vsrc = is_50hz ? 576 : 480; + + v4l2_dbg(1, debug, sd, "decoder set size to %ix%i\n", width, height); + + /* FIXME need better bounds checking here */ + if ((width < 1) || (width > 1440)) + return -EINVAL; + if ((height < 1) || (height > Vsrc)) + return -EINVAL; + + if (!saa711x_has_reg(state->ident, R_D0_B_HORIZ_PRESCALING)) { + /* Decoder only supports 720 columns and 480 or 576 lines */ + if (width != 720) + return -EINVAL; + if (height != Vsrc) + return -EINVAL; + } + + state->width = width; + state->height = height; + + if (!saa711x_has_reg(state->ident, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH)) + return 0; + + /* probably have a valid size, let's set it */ + /* Set output width/height */ + /* width */ + + saa711x_write(sd, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, + (u8) (width & 0xff)); + saa711x_write(sd, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, + (u8) ((width >> 8) & 0xff)); + + /* Vertical Scaling uses height/2 */ + res = height / 2; + + /* On 60Hz, it is using a higher Vertical Output Size */ + if (!is_50hz) + res += (VRES_60HZ - 480) >> 1; + + /* height */ + saa711x_write(sd, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH, + (u8) (res & 0xff)); + saa711x_write(sd, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB, + (u8) ((res >> 8) & 0xff)); + + /* Scaling settings */ + /* Hprescaler is floor(inres/outres) */ + HPSC = (int)(720 / width); + /* 0 is not allowed (div. by zero) */ + HPSC = HPSC ? HPSC : 1; + HFSC = (int)((1024 * 720) / (HPSC * width)); + /* FIXME hardcodes to "Task B" + * write H prescaler integer */ + saa711x_write(sd, R_D0_B_HORIZ_PRESCALING, + (u8) (HPSC & 0x3f)); + + v4l2_dbg(1, debug, sd, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC); + /* write H fine-scaling (luminance) */ + saa711x_write(sd, R_D8_B_HORIZ_LUMA_SCALING_INC, + (u8) (HFSC & 0xff)); + saa711x_write(sd, R_D9_B_HORIZ_LUMA_SCALING_INC_MSB, + (u8) ((HFSC >> 8) & 0xff)); + /* write H fine-scaling (chrominance) + * must be lum/2, so i'll just bitshift :) */ + saa711x_write(sd, R_DC_B_HORIZ_CHROMA_SCALING, + (u8) ((HFSC >> 1) & 0xff)); + saa711x_write(sd, R_DD_B_HORIZ_CHROMA_SCALING_MSB, + (u8) ((HFSC >> 9) & 0xff)); + + VSCY = (int)((1024 * Vsrc) / height); + v4l2_dbg(1, debug, sd, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY); + + /* Correct Contrast and Luminance */ + saa711x_write(sd, R_D5_B_LUMA_CONTRAST_CNTL, + (u8) (64 * 1024 / VSCY)); + saa711x_write(sd, R_D6_B_CHROMA_SATURATION_CNTL, + (u8) (64 * 1024 / VSCY)); + + /* write V fine-scaling (luminance) */ + saa711x_write(sd, R_E0_B_VERT_LUMA_SCALING_INC, + (u8) (VSCY & 0xff)); + saa711x_write(sd, R_E1_B_VERT_LUMA_SCALING_INC_MSB, + (u8) ((VSCY >> 8) & 0xff)); + /* write V fine-scaling (chrominance) */ + saa711x_write(sd, R_E2_B_VERT_CHROMA_SCALING_INC, + (u8) (VSCY & 0xff)); + saa711x_write(sd, R_E3_B_VERT_CHROMA_SCALING_INC_MSB, + (u8) ((VSCY >> 8) & 0xff)); + + saa711x_writeregs(sd, saa7115_cfg_reset_scaler); + + /* Activates task "B" */ + saa711x_write(sd, R_80_GLOBAL_CNTL_1, + saa711x_read(sd, R_80_GLOBAL_CNTL_1) | 0x20); + + return 0; +} + +static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct saa711x_state *state = to_state(sd); + + /* Prevent unnecessary standard changes. During a standard + change the I-Port is temporarily disabled. Any devices + reading from that port can get confused. + Note that s_std is also used to switch from + radio to TV mode, so if a s_std is broadcast to + all I2C devices then you do not want to have an unwanted + side-effect here. */ + if (std == state->std) + return; + + state->std = std; + + // This works for NTSC-M, SECAM-L and the 50Hz PAL variants. + if (std & V4L2_STD_525_60) { + v4l2_dbg(1, debug, sd, "decoder set standard 60 Hz\n"); + saa711x_writeregs(sd, saa7115_cfg_60hz_video); + saa711x_set_size(sd, 720, 480); + } else { + v4l2_dbg(1, debug, sd, "decoder set standard 50 Hz\n"); + saa711x_writeregs(sd, saa7115_cfg_50hz_video); + saa711x_set_size(sd, 720, 576); + } + + /* Register 0E - Bits D6-D4 on NO-AUTO mode + (SAA7111 and SAA7113 doesn't have auto mode) + 50 Hz / 625 lines 60 Hz / 525 lines + 000 PAL BGDHI (4.43Mhz) NTSC M (3.58MHz) + 001 NTSC 4.43 (50 Hz) PAL 4.43 (60 Hz) + 010 Combination-PAL N (3.58MHz) NTSC 4.43 (60 Hz) + 011 NTSC N (3.58MHz) PAL M (3.58MHz) + 100 reserved NTSC-Japan (3.58MHz) + */ + if (state->ident <= V4L2_IDENT_SAA7113) { + u8 reg = saa711x_read(sd, R_0E_CHROMA_CNTL_1) & 0x8f; + + if (std == V4L2_STD_PAL_M) { + reg |= 0x30; + } else if (std == V4L2_STD_PAL_Nc) { + reg |= 0x20; + } else if (std == V4L2_STD_PAL_60) { + reg |= 0x10; + } else if (std == V4L2_STD_NTSC_M_JP) { + reg |= 0x40; + } else if (std & V4L2_STD_SECAM) { + reg |= 0x50; + } + saa711x_write(sd, R_0E_CHROMA_CNTL_1, reg); + } else { + /* restart task B if needed */ + int taskb = saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10; + + if (taskb && state->ident == V4L2_IDENT_SAA7114) { + saa711x_writeregs(sd, saa7115_cfg_vbi_on); + } + + /* switch audio mode too! */ + saa711x_s_clock_freq(sd, state->audclk_freq); + } +} + +/* setup the sliced VBI lcr registers according to the sliced VBI format */ +static void saa711x_set_lcr(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt) +{ + struct saa711x_state *state = to_state(sd); + int is_50hz = (state->std & V4L2_STD_625_50); + u8 lcr[24]; + int i, x; + +#if 1 + /* saa7113/7114/7118 VBI support are experimental */ + if (!saa711x_has_reg(state->ident, R_41_LCR_BASE)) + return; + +#else + /* SAA7113 and SAA7118 also should support VBI - Need testing */ + if (state->ident != V4L2_IDENT_SAA7115) + return; +#endif + + for (i = 0; i <= 23; i++) + lcr[i] = 0xff; + + if (fmt == NULL) { + /* raw VBI */ + if (is_50hz) + for (i = 6; i <= 23; i++) + lcr[i] = 0xdd; + else + for (i = 10; i <= 21; i++) + lcr[i] = 0xdd; + } else { + /* sliced VBI */ + /* first clear lines that cannot be captured */ + if (is_50hz) { + for (i = 0; i <= 5; i++) + fmt->service_lines[0][i] = + fmt->service_lines[1][i] = 0; + } + else { + for (i = 0; i <= 9; i++) + fmt->service_lines[0][i] = + fmt->service_lines[1][i] = 0; + for (i = 22; i <= 23; i++) + fmt->service_lines[0][i] = + fmt->service_lines[1][i] = 0; + } + + /* Now set the lcr values according to the specified service */ + for (i = 6; i <= 23; i++) { + lcr[i] = 0; + for (x = 0; x <= 1; x++) { + switch (fmt->service_lines[1-x][i]) { + case 0: + lcr[i] |= 0xf << (4 * x); + break; + case V4L2_SLICED_TELETEXT_B: + lcr[i] |= 1 << (4 * x); + break; + case V4L2_SLICED_CAPTION_525: + lcr[i] |= 4 << (4 * x); + break; + case V4L2_SLICED_WSS_625: + lcr[i] |= 5 << (4 * x); + break; + case V4L2_SLICED_VPS: + lcr[i] |= 7 << (4 * x); + break; + } + } + } + } + + /* write the lcr registers */ + for (i = 2; i <= 23; i++) { + saa711x_write(sd, i - 2 + R_41_LCR_BASE, lcr[i]); + } + + /* enable/disable raw VBI capturing */ + saa711x_writeregs(sd, fmt == NULL ? + saa7115_cfg_vbi_on : + saa7115_cfg_vbi_off); +} + +static int saa711x_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *sliced) +{ + static u16 lcr2vbi[] = { + 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ + 0, V4L2_SLICED_CAPTION_525, /* 4 */ + V4L2_SLICED_WSS_625, 0, /* 5 */ + V4L2_SLICED_VPS, 0, 0, 0, 0, /* 7 */ + 0, 0, 0, 0 + }; + int i; + + memset(sliced, 0, sizeof(*sliced)); + /* done if using raw VBI */ + if (saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10) + return 0; + for (i = 2; i <= 23; i++) { + u8 v = saa711x_read(sd, i - 2 + R_41_LCR_BASE); + + sliced->service_lines[0][i] = lcr2vbi[v >> 4]; + sliced->service_lines[1][i] = lcr2vbi[v & 0xf]; + sliced->service_set |= + sliced->service_lines[0][i] | sliced->service_lines[1][i]; + } + return 0; +} + +static int saa711x_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) +{ + saa711x_set_lcr(sd, NULL); + return 0; +} + +static int saa711x_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt) +{ + saa711x_set_lcr(sd, fmt); + return 0; +} + +static int saa711x_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) +{ + if (fmt->code != V4L2_MBUS_FMT_FIXED) + return -EINVAL; + fmt->field = V4L2_FIELD_INTERLACED; + fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + return saa711x_set_size(sd, fmt->width, fmt->height); +} + +/* Decode the sliced VBI data stream as created by the saa7115. + The format is described in the saa7115 datasheet in Tables 25 and 26 + and in Figure 33. + The current implementation uses SAV/EAV codes and not the ancillary data + headers. The vbi->p pointer points to the R_5E_SDID byte right after the SAV + code. */ +static int saa711x_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi) +{ + struct saa711x_state *state = to_state(sd); + static const char vbi_no_data_pattern[] = { + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0 + }; + u8 *p = vbi->p; + u32 wss; + int id1, id2; /* the ID1 and ID2 bytes from the internal header */ + + vbi->type = 0; /* mark result as a failure */ + id1 = p[2]; + id2 = p[3]; + /* Note: the field bit is inverted for 60 Hz video */ + if (state->std & V4L2_STD_525_60) + id1 ^= 0x40; + + /* Skip internal header, p now points to the start of the payload */ + p += 4; + vbi->p = p; + + /* calculate field and line number of the VBI packet (1-23) */ + vbi->is_second_field = ((id1 & 0x40) != 0); + vbi->line = (id1 & 0x3f) << 3; + vbi->line |= (id2 & 0x70) >> 4; + + /* Obtain data type */ + id2 &= 0xf; + + /* If the VBI slicer does not detect any signal it will fill up + the payload buffer with 0xa0 bytes. */ + if (!memcmp(p, vbi_no_data_pattern, sizeof(vbi_no_data_pattern))) + return 0; + + /* decode payloads */ + switch (id2) { + case 1: + vbi->type = V4L2_SLICED_TELETEXT_B; + break; + case 4: + if (!saa711x_odd_parity(p[0]) || !saa711x_odd_parity(p[1])) + return 0; + vbi->type = V4L2_SLICED_CAPTION_525; + break; + case 5: + wss = saa711x_decode_wss(p); + if (wss == -1) + return 0; + p[0] = wss & 0xff; + p[1] = wss >> 8; + vbi->type = V4L2_SLICED_WSS_625; + break; + case 7: + if (saa711x_decode_vps(p, p) != 0) + return 0; + vbi->type = V4L2_SLICED_VPS; + break; + default: + break; + } + return 0; +} + +/* ============ SAA7115 AUDIO settings (end) ============= */ + +static int saa711x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct saa711x_state *state = to_state(sd); + int status; + + if (state->radio) + return 0; + status = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); + + v4l2_dbg(1, debug, sd, "status: 0x%02x\n", status); + vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0; + return 0; +} + +static int saa711x_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct saa711x_state *state = to_state(sd); + + state->radio = 0; + saa711x_set_v4lstd(sd, std); + return 0; +} + +static int saa711x_s_radio(struct v4l2_subdev *sd) +{ + struct saa711x_state *state = to_state(sd); + + state->radio = 1; + return 0; +} + +static int saa711x_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct saa711x_state *state = to_state(sd); + u8 mask = (state->ident <= V4L2_IDENT_SAA7111A) ? 0xf8 : 0xf0; + + v4l2_dbg(1, debug, sd, "decoder set input %d output %d\n", + input, output); + + /* saa7111/3 does not have these inputs */ + if (state->ident <= V4L2_IDENT_SAA7113 && + (input == SAA7115_COMPOSITE4 || + input == SAA7115_COMPOSITE5)) { + return -EINVAL; + } + if (input > SAA7115_SVIDEO3) + return -EINVAL; + if (state->input == input && state->output == output) + return 0; + v4l2_dbg(1, debug, sd, "now setting %s input %s output\n", + (input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite", + (output == SAA7115_IPORT_ON) ? "iport on" : "iport off"); + state->input = input; + + /* saa7111 has slightly different input numbering */ + if (state->ident <= V4L2_IDENT_SAA7111A) { + if (input >= SAA7115_COMPOSITE4) + input -= 2; + /* saa7111 specific */ + saa711x_write(sd, R_10_CHROMA_CNTL_2, + (saa711x_read(sd, R_10_CHROMA_CNTL_2) & 0x3f) | + ((output & 0xc0) ^ 0x40)); + saa711x_write(sd, R_13_RT_X_PORT_OUT_CNTL, + (saa711x_read(sd, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) | + ((output & 2) ? 0x0a : 0)); + } + + /* select mode */ + saa711x_write(sd, R_02_INPUT_CNTL_1, + (saa711x_read(sd, R_02_INPUT_CNTL_1) & mask) | + input); + + /* bypass chrominance trap for S-Video modes */ + saa711x_write(sd, R_09_LUMA_CNTL, + (saa711x_read(sd, R_09_LUMA_CNTL) & 0x7f) | + (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0)); + + state->output = output; + if (state->ident == V4L2_IDENT_SAA7114 || + state->ident == V4L2_IDENT_SAA7115) { + saa711x_write(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK, + (saa711x_read(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) | + (state->output & 0x01)); + } + return 0; +} + +static int saa711x_s_gpio(struct v4l2_subdev *sd, u32 val) +{ + struct saa711x_state *state = to_state(sd); + + if (state->ident > V4L2_IDENT_SAA7111A) + return -EINVAL; + saa711x_write(sd, 0x11, (saa711x_read(sd, 0x11) & 0x7f) | + (val ? 0x80 : 0)); + return 0; +} + +static int saa711x_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct saa711x_state *state = to_state(sd); + + v4l2_dbg(1, debug, sd, "%s output\n", + enable ? "enable" : "disable"); + + if (state->enable == enable) + return 0; + state->enable = enable; + if (!saa711x_has_reg(state->ident, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED)) + return 0; + saa711x_write(sd, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, state->enable); + return 0; +} + +static int saa711x_s_crystal_freq(struct v4l2_subdev *sd, u32 freq, u32 flags) +{ + struct saa711x_state *state = to_state(sd); + + if (freq != SAA7115_FREQ_32_11_MHZ && freq != SAA7115_FREQ_24_576_MHZ) + return -EINVAL; + state->crystal_freq = freq; + state->cgcdiv = (flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4; + state->ucgc = (flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0; + state->apll = (flags & SAA7115_FREQ_FL_APLL) ? 1 : 0; + saa711x_s_clock_freq(sd, state->audclk_freq); + return 0; +} + +static int saa711x_reset(struct v4l2_subdev *sd, u32 val) +{ + v4l2_dbg(1, debug, sd, "decoder RESET\n"); + saa711x_writeregs(sd, saa7115_cfg_reset_scaler); + return 0; +} + +static int saa711x_g_vbi_data(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *data) +{ + /* Note: the internal field ID is inverted for NTSC, + so data->field 0 maps to the saa7115 even field, + whereas for PAL it maps to the saa7115 odd field. */ + switch (data->id) { + case V4L2_SLICED_WSS_625: + if (saa711x_read(sd, 0x6b) & 0xc0) + return -EIO; + data->data[0] = saa711x_read(sd, 0x6c); + data->data[1] = saa711x_read(sd, 0x6d); + return 0; + case V4L2_SLICED_CAPTION_525: + if (data->field == 0) { + /* CC */ + if (saa711x_read(sd, 0x66) & 0x30) + return -EIO; + data->data[0] = saa711x_read(sd, 0x69); + data->data[1] = saa711x_read(sd, 0x6a); + return 0; + } + /* XDS */ + if (saa711x_read(sd, 0x66) & 0xc0) + return -EIO; + data->data[0] = saa711x_read(sd, 0x67); + data->data[1] = saa711x_read(sd, 0x68); + return 0; + default: + return -EINVAL; + } +} + +static int saa711x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + struct saa711x_state *state = to_state(sd); + int reg1f, reg1e; + + /* + * The V4L2 core already initializes std with all supported + * Standards. All driver needs to do is to mask it, to remove + * standards that don't apply from the mask + */ + + reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); + v4l2_dbg(1, debug, sd, "Status byte 2 (0x1f)=0x%02x\n", reg1f); + + /* horizontal/vertical not locked */ + if (reg1f & 0x40) + goto ret; + + if (reg1f & 0x20) + *std &= V4L2_STD_525_60; + else + *std &= V4L2_STD_625_50; + + if (state->ident != V4L2_IDENT_SAA7115) + goto ret; + + reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC); + + switch (reg1e & 0x03) { + case 1: + *std &= V4L2_STD_NTSC; + break; + case 2: + /* + * V4L2_STD_PAL just cover the european PAL standards. + * This is wrong, as the device could also be using an + * other PAL standard. + */ + *std &= V4L2_STD_PAL | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | + V4L2_STD_PAL_M | V4L2_STD_PAL_60; + break; + case 3: + *std &= V4L2_STD_SECAM; + break; + default: + /* Can't detect anything */ + break; + } + + v4l2_dbg(1, debug, sd, "Status byte 1 (0x1e)=0x%02x\n", reg1e); + +ret: + v4l2_dbg(1, debug, sd, "detected std mask = %08Lx\n", *std); + + return 0; +} + +static int saa711x_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + struct saa711x_state *state = to_state(sd); + int reg1e = 0x80; + int reg1f; + + *status = V4L2_IN_ST_NO_SIGNAL; + if (state->ident == V4L2_IDENT_SAA7115) + reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC); + reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); + if ((reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80) + *status = 0; + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int saa711x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = saa711x_read(sd, reg->reg & 0xff); + reg->size = 1; + return 0; +} + +static int saa711x_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + saa711x_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif + +static int saa711x_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct saa711x_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0); +} + +static int saa711x_log_status(struct v4l2_subdev *sd) +{ + struct saa711x_state *state = to_state(sd); + int reg1e, reg1f; + int signalOk; + int vcr; + + v4l2_info(sd, "Audio frequency: %d Hz\n", state->audclk_freq); + if (state->ident != V4L2_IDENT_SAA7115) { + /* status for the saa7114 */ + reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); + signalOk = (reg1f & 0xc1) == 0x81; + v4l2_info(sd, "Video signal: %s\n", signalOk ? "ok" : "bad"); + v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); + return 0; + } + + /* status for the saa7115 */ + reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC); + reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); + + signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80; + vcr = !(reg1f & 0x10); + + if (state->input >= 6) + v4l2_info(sd, "Input: S-Video %d\n", state->input - 6); + else + v4l2_info(sd, "Input: Composite %d\n", state->input); + v4l2_info(sd, "Video signal: %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad"); + v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); + + switch (reg1e & 0x03) { + case 1: + v4l2_info(sd, "Detected format: NTSC\n"); + break; + case 2: + v4l2_info(sd, "Detected format: PAL\n"); + break; + case 3: + v4l2_info(sd, "Detected format: SECAM\n"); + break; + default: + v4l2_info(sd, "Detected format: BW/No color\n"); + break; + } + v4l2_info(sd, "Width, Height: %d, %d\n", state->width, state->height); + v4l2_ctrl_handler_log_status(&state->hdl, sd->name); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops saa711x_ctrl_ops = { + .s_ctrl = saa711x_s_ctrl, + .g_volatile_ctrl = saa711x_g_volatile_ctrl, +}; + +static const struct v4l2_subdev_core_ops saa711x_core_ops = { + .log_status = saa711x_log_status, + .g_chip_ident = saa711x_g_chip_ident, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, + .s_std = saa711x_s_std, + .reset = saa711x_reset, + .s_gpio = saa711x_s_gpio, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = saa711x_g_register, + .s_register = saa711x_s_register, +#endif +}; + +static const struct v4l2_subdev_tuner_ops saa711x_tuner_ops = { + .s_radio = saa711x_s_radio, + .g_tuner = saa711x_g_tuner, +}; + +static const struct v4l2_subdev_audio_ops saa711x_audio_ops = { + .s_clock_freq = saa711x_s_clock_freq, +}; + +static const struct v4l2_subdev_video_ops saa711x_video_ops = { + .s_routing = saa711x_s_routing, + .s_crystal_freq = saa711x_s_crystal_freq, + .s_mbus_fmt = saa711x_s_mbus_fmt, + .s_stream = saa711x_s_stream, + .querystd = saa711x_querystd, + .g_input_status = saa711x_g_input_status, +}; + +static const struct v4l2_subdev_vbi_ops saa711x_vbi_ops = { + .g_vbi_data = saa711x_g_vbi_data, + .decode_vbi_line = saa711x_decode_vbi_line, + .g_sliced_fmt = saa711x_g_sliced_fmt, + .s_sliced_fmt = saa711x_s_sliced_fmt, + .s_raw_fmt = saa711x_s_raw_fmt, +}; + +static const struct v4l2_subdev_ops saa711x_ops = { + .core = &saa711x_core_ops, + .tuner = &saa711x_tuner_ops, + .audio = &saa711x_audio_ops, + .video = &saa711x_video_ops, + .vbi = &saa711x_vbi_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static int saa711x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct saa711x_state *state; + struct v4l2_subdev *sd; + struct v4l2_ctrl_handler *hdl; + int i; + char name[17]; + char chip_id; + int autodetect = !id || id->driver_data == 1; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + for (i = 0; i < 0x0f; i++) { + i2c_smbus_write_byte_data(client, 0, i); + name[i] = (i2c_smbus_read_byte_data(client, 0) & 0x0f) + '0'; + if (name[i] > '9') + name[i] += 'a' - '9' - 1; + } + name[i] = '\0'; + + chip_id = name[5]; + + /* Check whether this chip is part of the saa711x series */ + if (memcmp(name + 1, "f711", 4)) { + v4l_dbg(1, debug, client, "chip found @ 0x%x (ID %s) does not match a known saa711x chip.\n", + client->addr << 1, name); + return -ENODEV; + } + + /* Safety check */ + if (!autodetect && id->name[6] != chip_id) { + v4l_warn(client, "found saa711%c while %s was expected\n", + chip_id, id->name); + } + snprintf(client->name, sizeof(client->name), "saa711%c", chip_id); + v4l_info(client, "saa711%c found (%s) @ 0x%x (%s)\n", chip_id, name, + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct saa711x_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &saa711x_ops); + + hdl = &state->hdl; + v4l2_ctrl_handler_init(hdl, 6); + /* add in ascending ID order */ + v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, + V4L2_CID_CONTRAST, 0, 127, 1, 64); + v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, + V4L2_CID_SATURATION, 0, 127, 1, 64); + v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + state->agc = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, + V4L2_CID_CHROMA_AGC, 0, 1, 1, 1); + state->gain = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, + V4L2_CID_CHROMA_GAIN, 0, 127, 1, 40); + sd->ctrl_handler = hdl; + if (hdl->error) { + int err = hdl->error; + + v4l2_ctrl_handler_free(hdl); + kfree(state); + return err; + } + v4l2_ctrl_auto_cluster(2, &state->agc, 0, true); + + state->input = -1; + state->output = SAA7115_IPORT_ON; + state->enable = 1; + state->radio = 0; + switch (chip_id) { + case '1': + state->ident = V4L2_IDENT_SAA7111; + if (saa711x_read(sd, R_00_CHIP_VERSION) & 0xf0) { + v4l_info(client, "saa7111a variant found\n"); + state->ident = V4L2_IDENT_SAA7111A; + } + break; + case '3': + state->ident = V4L2_IDENT_SAA7113; + break; + case '4': + state->ident = V4L2_IDENT_SAA7114; + break; + case '5': + state->ident = V4L2_IDENT_SAA7115; + break; + case '8': + state->ident = V4L2_IDENT_SAA7118; + break; + default: + state->ident = V4L2_IDENT_SAA7111; + v4l2_info(sd, "WARNING: Chip is not known - Falling back to saa7111\n"); + break; + } + + state->audclk_freq = 48000; + + v4l2_dbg(1, debug, sd, "writing init values\n"); + + /* init to 60hz/48khz */ + state->crystal_freq = SAA7115_FREQ_24_576_MHZ; + switch (state->ident) { + case V4L2_IDENT_SAA7111: + case V4L2_IDENT_SAA7111A: + saa711x_writeregs(sd, saa7111_init); + break; + case V4L2_IDENT_SAA7113: + saa711x_writeregs(sd, saa7113_init); + break; + default: + state->crystal_freq = SAA7115_FREQ_32_11_MHZ; + saa711x_writeregs(sd, saa7115_init_auto_input); + } + if (state->ident > V4L2_IDENT_SAA7111A) + saa711x_writeregs(sd, saa7115_init_misc); + saa711x_set_v4lstd(sd, V4L2_STD_NTSC); + v4l2_ctrl_handler_setup(hdl); + + v4l2_dbg(1, debug, sd, "status: (1E) 0x%02x, (1F) 0x%02x\n", + saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC), + saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC)); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa711x_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); + kfree(to_state(sd)); + return 0; +} + +static const struct i2c_device_id saa711x_id[] = { + { "saa7115_auto", 1 }, /* autodetect */ + { "saa7111", 0 }, + { "saa7113", 0 }, + { "saa7114", 0 }, + { "saa7115", 0 }, + { "saa7118", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa711x_id); + +static struct i2c_driver saa711x_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "saa7115", + }, + .probe = saa711x_probe, + .remove = saa711x_remove, + .id_table = saa711x_id, +}; + +module_i2c_driver(saa711x_driver); diff --git a/drivers/media/i2c/saa711x_regs.h b/drivers/media/i2c/saa711x_regs.h new file mode 100644 index 00000000000..4e5f2eb0a2c --- /dev/null +++ b/drivers/media/i2c/saa711x_regs.h @@ -0,0 +1,549 @@ +/* saa711x - Philips SAA711x video decoder register specifications + * + * Copyright (c) 2006 Mauro Carvalho Chehab + * + * 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. + */ + +#define R_00_CHIP_VERSION 0x00 +/* Video Decoder */ + /* Video Decoder - Frontend part */ +#define R_01_INC_DELAY 0x01 +#define R_02_INPUT_CNTL_1 0x02 +#define R_03_INPUT_CNTL_2 0x03 +#define R_04_INPUT_CNTL_3 0x04 +#define R_05_INPUT_CNTL_4 0x05 + /* Video Decoder - Decoder part */ +#define R_06_H_SYNC_START 0x06 +#define R_07_H_SYNC_STOP 0x07 +#define R_08_SYNC_CNTL 0x08 +#define R_09_LUMA_CNTL 0x09 +#define R_0A_LUMA_BRIGHT_CNTL 0x0a +#define R_0B_LUMA_CONTRAST_CNTL 0x0b +#define R_0C_CHROMA_SAT_CNTL 0x0c +#define R_0D_CHROMA_HUE_CNTL 0x0d +#define R_0E_CHROMA_CNTL_1 0x0e +#define R_0F_CHROMA_GAIN_CNTL 0x0f +#define R_10_CHROMA_CNTL_2 0x10 +#define R_11_MODE_DELAY_CNTL 0x11 +#define R_12_RT_SIGNAL_CNTL 0x12 +#define R_13_RT_X_PORT_OUT_CNTL 0x13 +#define R_14_ANAL_ADC_COMPAT_CNTL 0x14 +#define R_15_VGATE_START_FID_CHG 0x15 +#define R_16_VGATE_STOP 0x16 +#define R_17_MISC_VGATE_CONF_AND_MSB 0x17 +#define R_18_RAW_DATA_GAIN_CNTL 0x18 +#define R_19_RAW_DATA_OFF_CNTL 0x19 +#define R_1A_COLOR_KILL_LVL_CNTL 0x1a +#define R_1B_MISC_TVVCRDET 0x1b +#define R_1C_ENHAN_COMB_CTRL1 0x1c +#define R_1D_ENHAN_COMB_CTRL2 0x1d +#define R_1E_STATUS_BYTE_1_VD_DEC 0x1e +#define R_1F_STATUS_BYTE_2_VD_DEC 0x1f + +/* Component processing and interrupt masking part */ +#define R_23_INPUT_CNTL_5 0x23 +#define R_24_INPUT_CNTL_6 0x24 +#define R_25_INPUT_CNTL_7 0x25 +#define R_29_COMP_DELAY 0x29 +#define R_2A_COMP_BRIGHT_CNTL 0x2a +#define R_2B_COMP_CONTRAST_CNTL 0x2b +#define R_2C_COMP_SAT_CNTL 0x2c +#define R_2D_INTERRUPT_MASK_1 0x2d +#define R_2E_INTERRUPT_MASK_2 0x2e +#define R_2F_INTERRUPT_MASK_3 0x2f + +/* Audio clock generator part */ +#define R_30_AUD_MAST_CLK_CYCLES_PER_FIELD 0x30 +#define R_34_AUD_MAST_CLK_NOMINAL_INC 0x34 +#define R_38_CLK_RATIO_AMXCLK_TO_ASCLK 0x38 +#define R_39_CLK_RATIO_ASCLK_TO_ALRCLK 0x39 +#define R_3A_AUD_CLK_GEN_BASIC_SETUP 0x3a + +/* General purpose VBI data slicer part */ +#define R_40_SLICER_CNTL_1 0x40 +#define R_41_LCR_BASE 0x41 +#define R_58_PROGRAM_FRAMING_CODE 0x58 +#define R_59_H_OFF_FOR_SLICER 0x59 +#define R_5A_V_OFF_FOR_SLICER 0x5a +#define R_5B_FLD_OFF_AND_MSB_FOR_H_AND_V_OFF 0x5b +#define R_5D_DID 0x5d +#define R_5E_SDID 0x5e +#define R_60_SLICER_STATUS_BYTE_0 0x60 +#define R_61_SLICER_STATUS_BYTE_1 0x61 +#define R_62_SLICER_STATUS_BYTE_2 0x62 + +/* X port, I port and the scaler part */ + /* Task independent global settings */ +#define R_80_GLOBAL_CNTL_1 0x80 +#define R_81_V_SYNC_FLD_ID_SRC_SEL_AND_RETIMED_V_F 0x81 +#define R_83_X_PORT_I_O_ENA_AND_OUT_CLK 0x83 +#define R_84_I_PORT_SIGNAL_DEF 0x84 +#define R_85_I_PORT_SIGNAL_POLAR 0x85 +#define R_86_I_PORT_FIFO_FLAG_CNTL_AND_ARBIT 0x86 +#define R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED 0x87 +#define R_88_POWER_SAVE_ADC_PORT_CNTL 0x88 +#define R_8F_STATUS_INFO_SCALER 0x8f + /* Task A definition */ + /* Basic settings and acquisition window definition */ +#define R_90_A_TASK_HANDLING_CNTL 0x90 +#define R_91_A_X_PORT_FORMATS_AND_CONF 0x91 +#define R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL 0x92 +#define R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF 0x93 +#define R_94_A_HORIZ_INPUT_WINDOW_START 0x94 +#define R_95_A_HORIZ_INPUT_WINDOW_START_MSB 0x95 +#define R_96_A_HORIZ_INPUT_WINDOW_LENGTH 0x96 +#define R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB 0x97 +#define R_98_A_VERT_INPUT_WINDOW_START 0x98 +#define R_99_A_VERT_INPUT_WINDOW_START_MSB 0x99 +#define R_9A_A_VERT_INPUT_WINDOW_LENGTH 0x9a +#define R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB 0x9b +#define R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH 0x9c +#define R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB 0x9d +#define R_9E_A_VERT_OUTPUT_WINDOW_LENGTH 0x9e +#define R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB 0x9f + /* FIR filtering and prescaling */ +#define R_A0_A_HORIZ_PRESCALING 0xa0 +#define R_A1_A_ACCUMULATION_LENGTH 0xa1 +#define R_A2_A_PRESCALER_DC_GAIN_AND_FIR_PREFILTER 0xa2 +#define R_A4_A_LUMA_BRIGHTNESS_CNTL 0xa4 +#define R_A5_A_LUMA_CONTRAST_CNTL 0xa5 +#define R_A6_A_CHROMA_SATURATION_CNTL 0xa6 + /* Horizontal phase scaling */ +#define R_A8_A_HORIZ_LUMA_SCALING_INC 0xa8 +#define R_A9_A_HORIZ_LUMA_SCALING_INC_MSB 0xa9 +#define R_AA_A_HORIZ_LUMA_PHASE_OFF 0xaa +#define R_AC_A_HORIZ_CHROMA_SCALING_INC 0xac +#define R_AD_A_HORIZ_CHROMA_SCALING_INC_MSB 0xad +#define R_AE_A_HORIZ_CHROMA_PHASE_OFF 0xae +#define R_AF_A_HORIZ_CHROMA_PHASE_OFF_MSB 0xaf + /* Vertical scaling */ +#define R_B0_A_VERT_LUMA_SCALING_INC 0xb0 +#define R_B1_A_VERT_LUMA_SCALING_INC_MSB 0xb1 +#define R_B2_A_VERT_CHROMA_SCALING_INC 0xb2 +#define R_B3_A_VERT_CHROMA_SCALING_INC_MSB 0xb3 +#define R_B4_A_VERT_SCALING_MODE_CNTL 0xb4 +#define R_B8_A_VERT_CHROMA_PHASE_OFF_00 0xb8 +#define R_B9_A_VERT_CHROMA_PHASE_OFF_01 0xb9 +#define R_BA_A_VERT_CHROMA_PHASE_OFF_10 0xba +#define R_BB_A_VERT_CHROMA_PHASE_OFF_11 0xbb +#define R_BC_A_VERT_LUMA_PHASE_OFF_00 0xbc +#define R_BD_A_VERT_LUMA_PHASE_OFF_01 0xbd +#define R_BE_A_VERT_LUMA_PHASE_OFF_10 0xbe +#define R_BF_A_VERT_LUMA_PHASE_OFF_11 0xbf + /* Task B definition */ + /* Basic settings and acquisition window definition */ +#define R_C0_B_TASK_HANDLING_CNTL 0xc0 +#define R_C1_B_X_PORT_FORMATS_AND_CONF 0xc1 +#define R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION 0xc2 +#define R_C3_B_I_PORT_FORMATS_AND_CONF 0xc3 +#define R_C4_B_HORIZ_INPUT_WINDOW_START 0xc4 +#define R_C5_B_HORIZ_INPUT_WINDOW_START_MSB 0xc5 +#define R_C6_B_HORIZ_INPUT_WINDOW_LENGTH 0xc6 +#define R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB 0xc7 +#define R_C8_B_VERT_INPUT_WINDOW_START 0xc8 +#define R_C9_B_VERT_INPUT_WINDOW_START_MSB 0xc9 +#define R_CA_B_VERT_INPUT_WINDOW_LENGTH 0xca +#define R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB 0xcb +#define R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH 0xcc +#define R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB 0xcd +#define R_CE_B_VERT_OUTPUT_WINDOW_LENGTH 0xce +#define R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB 0xcf + /* FIR filtering and prescaling */ +#define R_D0_B_HORIZ_PRESCALING 0xd0 +#define R_D1_B_ACCUMULATION_LENGTH 0xd1 +#define R_D2_B_PRESCALER_DC_GAIN_AND_FIR_PREFILTER 0xd2 +#define R_D4_B_LUMA_BRIGHTNESS_CNTL 0xd4 +#define R_D5_B_LUMA_CONTRAST_CNTL 0xd5 +#define R_D6_B_CHROMA_SATURATION_CNTL 0xd6 + /* Horizontal phase scaling */ +#define R_D8_B_HORIZ_LUMA_SCALING_INC 0xd8 +#define R_D9_B_HORIZ_LUMA_SCALING_INC_MSB 0xd9 +#define R_DA_B_HORIZ_LUMA_PHASE_OFF 0xda +#define R_DC_B_HORIZ_CHROMA_SCALING 0xdc +#define R_DD_B_HORIZ_CHROMA_SCALING_MSB 0xdd +#define R_DE_B_HORIZ_PHASE_OFFSET_CRHOMA 0xde + /* Vertical scaling */ +#define R_E0_B_VERT_LUMA_SCALING_INC 0xe0 +#define R_E1_B_VERT_LUMA_SCALING_INC_MSB 0xe1 +#define R_E2_B_VERT_CHROMA_SCALING_INC 0xe2 +#define R_E3_B_VERT_CHROMA_SCALING_INC_MSB 0xe3 +#define R_E4_B_VERT_SCALING_MODE_CNTL 0xe4 +#define R_E8_B_VERT_CHROMA_PHASE_OFF_00 0xe8 +#define R_E9_B_VERT_CHROMA_PHASE_OFF_01 0xe9 +#define R_EA_B_VERT_CHROMA_PHASE_OFF_10 0xea +#define R_EB_B_VERT_CHROMA_PHASE_OFF_11 0xeb +#define R_EC_B_VERT_LUMA_PHASE_OFF_00 0xec +#define R_ED_B_VERT_LUMA_PHASE_OFF_01 0xed +#define R_EE_B_VERT_LUMA_PHASE_OFF_10 0xee +#define R_EF_B_VERT_LUMA_PHASE_OFF_11 0xef + +/* second PLL (PLL2) and Pulsegenerator Programming */ +#define R_F0_LFCO_PER_LINE 0xf0 +#define R_F1_P_I_PARAM_SELECT 0xf1 +#define R_F2_NOMINAL_PLL2_DTO 0xf2 +#define R_F3_PLL_INCREMENT 0xf3 +#define R_F4_PLL2_STATUS 0xf4 +#define R_F5_PULSGEN_LINE_LENGTH 0xf5 +#define R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG 0xf6 +#define R_F7_PULSE_A_POS_MSB 0xf7 +#define R_F8_PULSE_B_POS 0xf8 +#define R_F9_PULSE_B_POS_MSB 0xf9 +#define R_FA_PULSE_C_POS 0xfa +#define R_FB_PULSE_C_POS_MSB 0xfb +#define R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES 0xff + +#if 0 +/* Those structs will be used in the future for debug purposes */ +struct saa711x_reg_descr { + u8 reg; + int count; + char *name; +}; + +struct saa711x_reg_descr saa711x_regs[] = { + /* REG COUNT NAME */ + {R_00_CHIP_VERSION,1, + "Chip version"}, + + /* Video Decoder: R_01_INC_DELAY to R_1F_STATUS_BYTE_2_VD_DEC */ + + /* Video Decoder - Frontend part: R_01_INC_DELAY to R_05_INPUT_CNTL_4 */ + {R_01_INC_DELAY,1, + "Increment delay"}, + {R_02_INPUT_CNTL_1,1, + "Analog input control 1"}, + {R_03_INPUT_CNTL_2,1, + "Analog input control 2"}, + {R_04_INPUT_CNTL_3,1, + "Analog input control 3"}, + {R_05_INPUT_CNTL_4,1, + "Analog input control 4"}, + + /* Video Decoder - Decoder part: R_06_H_SYNC_START to R_1F_STATUS_BYTE_2_VD_DEC */ + {R_06_H_SYNC_START,1, + "Horizontal sync start"}, + {R_07_H_SYNC_STOP,1, + "Horizontal sync stop"}, + {R_08_SYNC_CNTL,1, + "Sync control"}, + {R_09_LUMA_CNTL,1, + "Luminance control"}, + {R_0A_LUMA_BRIGHT_CNTL,1, + "Luminance brightness control"}, + {R_0B_LUMA_CONTRAST_CNTL,1, + "Luminance contrast control"}, + {R_0C_CHROMA_SAT_CNTL,1, + "Chrominance saturation control"}, + {R_0D_CHROMA_HUE_CNTL,1, + "Chrominance hue control"}, + {R_0E_CHROMA_CNTL_1,1, + "Chrominance control 1"}, + {R_0F_CHROMA_GAIN_CNTL,1, + "Chrominance gain control"}, + {R_10_CHROMA_CNTL_2,1, + "Chrominance control 2"}, + {R_11_MODE_DELAY_CNTL,1, + "Mode/delay control"}, + {R_12_RT_SIGNAL_CNTL,1, + "RT signal control"}, + {R_13_RT_X_PORT_OUT_CNTL,1, + "RT/X port output control"}, + {R_14_ANAL_ADC_COMPAT_CNTL,1, + "Analog/ADC/compatibility control"}, + {R_15_VGATE_START_FID_CHG, 1, + "VGATE start FID change"}, + {R_16_VGATE_STOP,1, + "VGATE stop"}, + {R_17_MISC_VGATE_CONF_AND_MSB, 1, + "Miscellaneous VGATE configuration and MSBs"}, + {R_18_RAW_DATA_GAIN_CNTL,1, + "Raw data gain control",}, + {R_19_RAW_DATA_OFF_CNTL,1, + "Raw data offset control",}, + {R_1A_COLOR_KILL_LVL_CNTL,1, + "Color Killer Level Control"}, + { R_1B_MISC_TVVCRDET, 1, + "MISC /TVVCRDET"}, + { R_1C_ENHAN_COMB_CTRL1, 1, + "Enhanced comb ctrl1"}, + { R_1D_ENHAN_COMB_CTRL2, 1, + "Enhanced comb ctrl1"}, + {R_1E_STATUS_BYTE_1_VD_DEC,1, + "Status byte 1 video decoder"}, + {R_1F_STATUS_BYTE_2_VD_DEC,1, + "Status byte 2 video decoder"}, + + /* Component processing and interrupt masking part: 0x20h to R_2F_INTERRUPT_MASK_3 */ + /* 0x20 to 0x22 - Reserved */ + {R_23_INPUT_CNTL_5,1, + "Analog input control 5"}, + {R_24_INPUT_CNTL_6,1, + "Analog input control 6"}, + {R_25_INPUT_CNTL_7,1, + "Analog input control 7"}, + /* 0x26 to 0x28 - Reserved */ + {R_29_COMP_DELAY,1, + "Component delay"}, + {R_2A_COMP_BRIGHT_CNTL,1, + "Component brightness control"}, + {R_2B_COMP_CONTRAST_CNTL,1, + "Component contrast control"}, + {R_2C_COMP_SAT_CNTL,1, + "Component saturation control"}, + {R_2D_INTERRUPT_MASK_1,1, + "Interrupt mask 1"}, + {R_2E_INTERRUPT_MASK_2,1, + "Interrupt mask 2"}, + {R_2F_INTERRUPT_MASK_3,1, + "Interrupt mask 3"}, + + /* Audio clock generator part: R_30_AUD_MAST_CLK_CYCLES_PER_FIELD to 0x3f */ + {R_30_AUD_MAST_CLK_CYCLES_PER_FIELD,3, + "Audio master clock cycles per field"}, + /* 0x33 - Reserved */ + {R_34_AUD_MAST_CLK_NOMINAL_INC,3, + "Audio master clock nominal increment"}, + /* 0x37 - Reserved */ + {R_38_CLK_RATIO_AMXCLK_TO_ASCLK,1, + "Clock ratio AMXCLK to ASCLK"}, + {R_39_CLK_RATIO_ASCLK_TO_ALRCLK,1, + "Clock ratio ASCLK to ALRCLK"}, + {R_3A_AUD_CLK_GEN_BASIC_SETUP,1, + "Audio clock generator basic setup"}, + /* 0x3b-0x3f - Reserved */ + + /* General purpose VBI data slicer part: R_40_SLICER_CNTL_1 to 0x7f */ + {R_40_SLICER_CNTL_1,1, + "Slicer control 1"}, + {R_41_LCR,23, + "R_41_LCR"}, + {R_58_PROGRAM_FRAMING_CODE,1, + "Programmable framing code"}, + {R_59_H_OFF_FOR_SLICER,1, + "Horizontal offset for slicer"}, + {R_5A_V_OFF_FOR_SLICER,1, + "Vertical offset for slicer"}, + {R_5B_FLD_OFF_AND_MSB_FOR_H_AND_V_OFF,1, + "Field offset and MSBs for horizontal and vertical offset"}, + {R_5D_DID,1, + "Header and data identification (R_5D_DID)"}, + {R_5E_SDID,1, + "Sliced data identification (R_5E_SDID) code"}, + {R_60_SLICER_STATUS_BYTE_0,1, + "Slicer status byte 0"}, + {R_61_SLICER_STATUS_BYTE_1,1, + "Slicer status byte 1"}, + {R_62_SLICER_STATUS_BYTE_2,1, + "Slicer status byte 2"}, + /* 0x63-0x7f - Reserved */ + + /* X port, I port and the scaler part: R_80_GLOBAL_CNTL_1 to R_EF_B_VERT_LUMA_PHASE_OFF_11 */ + /* Task independent global settings: R_80_GLOBAL_CNTL_1 to R_8F_STATUS_INFO_SCALER */ + {R_80_GLOBAL_CNTL_1,1, + "Global control 1"}, + {R_81_V_SYNC_FLD_ID_SRC_SEL_AND_RETIMED_V_F,1, + "Vertical sync and Field ID source selection, retimed V and F signals"}, + /* 0x82 - Reserved */ + {R_83_X_PORT_I_O_ENA_AND_OUT_CLK,1, + "X port I/O enable and output clock"}, + {R_84_I_PORT_SIGNAL_DEF,1, + "I port signal definitions"}, + {R_85_I_PORT_SIGNAL_POLAR,1, + "I port signal polarities"}, + {R_86_I_PORT_FIFO_FLAG_CNTL_AND_ARBIT,1, + "I port FIFO flag control and arbitration"}, + {R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 1, + "I port I/O enable output clock and gated"}, + {R_88_POWER_SAVE_ADC_PORT_CNTL,1, + "Power save/ADC port control"}, + /* 089-0x8e - Reserved */ + {R_8F_STATUS_INFO_SCALER,1, + "Status information scaler part"}, + + /* Task A definition: R_90_A_TASK_HANDLING_CNTL to R_BF_A_VERT_LUMA_PHASE_OFF_11 */ + /* Task A: Basic settings and acquisition window definition */ + {R_90_A_TASK_HANDLING_CNTL,1, + "Task A: Task handling control"}, + {R_91_A_X_PORT_FORMATS_AND_CONF,1, + "Task A: X port formats and configuration"}, + {R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL,1, + "Task A: X port input reference signal definition"}, + {R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF,1, + "Task A: I port output formats and configuration"}, + {R_94_A_HORIZ_INPUT_WINDOW_START,2, + "Task A: Horizontal input window start"}, + {R_96_A_HORIZ_INPUT_WINDOW_LENGTH,2, + "Task A: Horizontal input window length"}, + {R_98_A_VERT_INPUT_WINDOW_START,2, + "Task A: Vertical input window start"}, + {R_9A_A_VERT_INPUT_WINDOW_LENGTH,2, + "Task A: Vertical input window length"}, + {R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH,2, + "Task A: Horizontal output window length"}, + {R_9E_A_VERT_OUTPUT_WINDOW_LENGTH,2, + "Task A: Vertical output window length"}, + + /* Task A: FIR filtering and prescaling */ + {R_A0_A_HORIZ_PRESCALING,1, + "Task A: Horizontal prescaling"}, + {R_A1_A_ACCUMULATION_LENGTH,1, + "Task A: Accumulation length"}, + {R_A2_A_PRESCALER_DC_GAIN_AND_FIR_PREFILTER,1, + "Task A: Prescaler DC gain and FIR prefilter"}, + /* 0xa3 - Reserved */ + {R_A4_A_LUMA_BRIGHTNESS_CNTL,1, + "Task A: Luminance brightness control"}, + {R_A5_A_LUMA_CONTRAST_CNTL,1, + "Task A: Luminance contrast control"}, + {R_A6_A_CHROMA_SATURATION_CNTL,1, + "Task A: Chrominance saturation control"}, + /* 0xa7 - Reserved */ + + /* Task A: Horizontal phase scaling */ + {R_A8_A_HORIZ_LUMA_SCALING_INC,2, + "Task A: Horizontal luminance scaling increment"}, + {R_AA_A_HORIZ_LUMA_PHASE_OFF,1, + "Task A: Horizontal luminance phase offset"}, + /* 0xab - Reserved */ + {R_AC_A_HORIZ_CHROMA_SCALING_INC,2, + "Task A: Horizontal chrominance scaling increment"}, + {R_AE_A_HORIZ_CHROMA_PHASE_OFF,1, + "Task A: Horizontal chrominance phase offset"}, + /* 0xaf - Reserved */ + + /* Task A: Vertical scaling */ + {R_B0_A_VERT_LUMA_SCALING_INC,2, + "Task A: Vertical luminance scaling increment"}, + {R_B2_A_VERT_CHROMA_SCALING_INC,2, + "Task A: Vertical chrominance scaling increment"}, + {R_B4_A_VERT_SCALING_MODE_CNTL,1, + "Task A: Vertical scaling mode control"}, + /* 0xb5-0xb7 - Reserved */ + {R_B8_A_VERT_CHROMA_PHASE_OFF_00,1, + "Task A: Vertical chrominance phase offset '00'"}, + {R_B9_A_VERT_CHROMA_PHASE_OFF_01,1, + "Task A: Vertical chrominance phase offset '01'"}, + {R_BA_A_VERT_CHROMA_PHASE_OFF_10,1, + "Task A: Vertical chrominance phase offset '10'"}, + {R_BB_A_VERT_CHROMA_PHASE_OFF_11,1, + "Task A: Vertical chrominance phase offset '11'"}, + {R_BC_A_VERT_LUMA_PHASE_OFF_00,1, + "Task A: Vertical luminance phase offset '00'"}, + {R_BD_A_VERT_LUMA_PHASE_OFF_01,1, + "Task A: Vertical luminance phase offset '01'"}, + {R_BE_A_VERT_LUMA_PHASE_OFF_10,1, + "Task A: Vertical luminance phase offset '10'"}, + {R_BF_A_VERT_LUMA_PHASE_OFF_11,1, + "Task A: Vertical luminance phase offset '11'"}, + + /* Task B definition: R_C0_B_TASK_HANDLING_CNTL to R_EF_B_VERT_LUMA_PHASE_OFF_11 */ + /* Task B: Basic settings and acquisition window definition */ + {R_C0_B_TASK_HANDLING_CNTL,1, + "Task B: Task handling control"}, + {R_C1_B_X_PORT_FORMATS_AND_CONF,1, + "Task B: X port formats and configuration"}, + {R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION,1, + "Task B: Input reference signal definition"}, + {R_C3_B_I_PORT_FORMATS_AND_CONF,1, + "Task B: I port formats and configuration"}, + {R_C4_B_HORIZ_INPUT_WINDOW_START,2, + "Task B: Horizontal input window start"}, + {R_C6_B_HORIZ_INPUT_WINDOW_LENGTH,2, + "Task B: Horizontal input window length"}, + {R_C8_B_VERT_INPUT_WINDOW_START,2, + "Task B: Vertical input window start"}, + {R_CA_B_VERT_INPUT_WINDOW_LENGTH,2, + "Task B: Vertical input window length"}, + {R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH,2, + "Task B: Horizontal output window length"}, + {R_CE_B_VERT_OUTPUT_WINDOW_LENGTH,2, + "Task B: Vertical output window length"}, + + /* Task B: FIR filtering and prescaling */ + {R_D0_B_HORIZ_PRESCALING,1, + "Task B: Horizontal prescaling"}, + {R_D1_B_ACCUMULATION_LENGTH,1, + "Task B: Accumulation length"}, + {R_D2_B_PRESCALER_DC_GAIN_AND_FIR_PREFILTER,1, + "Task B: Prescaler DC gain and FIR prefilter"}, + /* 0xd3 - Reserved */ + {R_D4_B_LUMA_BRIGHTNESS_CNTL,1, + "Task B: Luminance brightness control"}, + {R_D5_B_LUMA_CONTRAST_CNTL,1, + "Task B: Luminance contrast control"}, + {R_D6_B_CHROMA_SATURATION_CNTL,1, + "Task B: Chrominance saturation control"}, + /* 0xd7 - Reserved */ + + /* Task B: Horizontal phase scaling */ + {R_D8_B_HORIZ_LUMA_SCALING_INC,2, + "Task B: Horizontal luminance scaling increment"}, + {R_DA_B_HORIZ_LUMA_PHASE_OFF,1, + "Task B: Horizontal luminance phase offset"}, + /* 0xdb - Reserved */ + {R_DC_B_HORIZ_CHROMA_SCALING,2, + "Task B: Horizontal chrominance scaling"}, + {R_DE_B_HORIZ_PHASE_OFFSET_CRHOMA,1, + "Task B: Horizontal Phase Offset Chroma"}, + /* 0xdf - Reserved */ + + /* Task B: Vertical scaling */ + {R_E0_B_VERT_LUMA_SCALING_INC,2, + "Task B: Vertical luminance scaling increment"}, + {R_E2_B_VERT_CHROMA_SCALING_INC,2, + "Task B: Vertical chrominance scaling increment"}, + {R_E4_B_VERT_SCALING_MODE_CNTL,1, + "Task B: Vertical scaling mode control"}, + /* 0xe5-0xe7 - Reserved */ + {R_E8_B_VERT_CHROMA_PHASE_OFF_00,1, + "Task B: Vertical chrominance phase offset '00'"}, + {R_E9_B_VERT_CHROMA_PHASE_OFF_01,1, + "Task B: Vertical chrominance phase offset '01'"}, + {R_EA_B_VERT_CHROMA_PHASE_OFF_10,1, + "Task B: Vertical chrominance phase offset '10'"}, + {R_EB_B_VERT_CHROMA_PHASE_OFF_11,1, + "Task B: Vertical chrominance phase offset '11'"}, + {R_EC_B_VERT_LUMA_PHASE_OFF_00,1, + "Task B: Vertical luminance phase offset '00'"}, + {R_ED_B_VERT_LUMA_PHASE_OFF_01,1, + "Task B: Vertical luminance phase offset '01'"}, + {R_EE_B_VERT_LUMA_PHASE_OFF_10,1, + "Task B: Vertical luminance phase offset '10'"}, + {R_EF_B_VERT_LUMA_PHASE_OFF_11,1, + "Task B: Vertical luminance phase offset '11'"}, + + /* second PLL (PLL2) and Pulsegenerator Programming */ + { R_F0_LFCO_PER_LINE, 1, + "LFCO's per line"}, + { R_F1_P_I_PARAM_SELECT,1, + "P-/I- Param. Select., PLL Mode, PLL H-Src., LFCO's per line"}, + { R_F2_NOMINAL_PLL2_DTO,1, + "Nominal PLL2 DTO"}, + {R_F3_PLL_INCREMENT,1, + "PLL2 Increment"}, + {R_F4_PLL2_STATUS,1, + "PLL2 Status"}, + {R_F5_PULSGEN_LINE_LENGTH,1, + "Pulsgen. line length"}, + {R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG,1, + "Pulse A Position, Pulsgen Resync., Pulsgen. H-Src., Pulsgen. line length"}, + {R_F7_PULSE_A_POS_MSB,1, + "Pulse A Position"}, + {R_F8_PULSE_B_POS,2, + "Pulse B Position"}, + {R_FA_PULSE_C_POS,2, + "Pulse C Position"}, + /* 0xfc to 0xfe - Reserved */ + {R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES,1, + "S_PLL max. phase, error threshold, PLL2 no. of lines, threshold"}, +}; +#endif diff --git a/drivers/media/i2c/saa7127.c b/drivers/media/i2c/saa7127.c new file mode 100644 index 00000000000..8ecb6564a31 --- /dev/null +++ b/drivers/media/i2c/saa7127.c @@ -0,0 +1,852 @@ +/* + * saa7127 - Philips SAA7127/SAA7129 video encoder driver + * + * Copyright (C) 2003 Roy Bulter + * + * Based on SAA7126 video encoder driver by Gillem & Andreas Oberritter + * + * Copyright (C) 2000-2001 Gillem + * Copyright (C) 2002 Andreas Oberritter + * + * Based on Stadis 4:2:2 MPEG-2 Decoder Driver by Nathan Laredo + * + * Copyright (C) 1999 Nathan Laredo + * + * This driver is designed for the Hauppauge 250/350 Linux driver + * from the ivtv Project + * + * Copyright (C) 2003 Kevin Thayer + * + * Dual output support: + * Copyright (C) 2004 Eric Varsanyi + * + * NTSC Tuning and 7.5 IRE Setup + * Copyright (C) 2004 Chris Kennedy + * + * VBI additions & cleanup: + * Copyright (C) 2004, 2005 Hans Verkuil + * + * Note: the saa7126 is identical to the saa7127, and the saa7128 is + * identical to the saa7129, except that the saa7126 and saa7128 have + * macrovision anti-taping support. This driver will almost certainly + * work fine for those chips, except of course for the missing anti-taping + * support. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +static int debug; +static int test_image; + +MODULE_DESCRIPTION("Philips SAA7127/9 video encoder driver"); +MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil"); +MODULE_LICENSE("GPL"); +module_param(debug, int, 0644); +module_param(test_image, int, 0644); +MODULE_PARM_DESC(debug, "debug level (0-2)"); +MODULE_PARM_DESC(test_image, "test_image (0-1)"); + + +/* + * SAA7127 registers + */ + +#define SAA7127_REG_STATUS 0x00 +#define SAA7127_REG_WIDESCREEN_CONFIG 0x26 +#define SAA7127_REG_WIDESCREEN_ENABLE 0x27 +#define SAA7127_REG_BURST_START 0x28 +#define SAA7127_REG_BURST_END 0x29 +#define SAA7127_REG_COPYGEN_0 0x2a +#define SAA7127_REG_COPYGEN_1 0x2b +#define SAA7127_REG_COPYGEN_2 0x2c +#define SAA7127_REG_OUTPUT_PORT_CONTROL 0x2d +#define SAA7127_REG_GAIN_LUMINANCE_RGB 0x38 +#define SAA7127_REG_GAIN_COLORDIFF_RGB 0x39 +#define SAA7127_REG_INPUT_PORT_CONTROL_1 0x3a +#define SAA7129_REG_FADE_KEY_COL2 0x4f +#define SAA7127_REG_CHROMA_PHASE 0x5a +#define SAA7127_REG_GAINU 0x5b +#define SAA7127_REG_GAINV 0x5c +#define SAA7127_REG_BLACK_LEVEL 0x5d +#define SAA7127_REG_BLANKING_LEVEL 0x5e +#define SAA7127_REG_VBI_BLANKING 0x5f +#define SAA7127_REG_DAC_CONTROL 0x61 +#define SAA7127_REG_BURST_AMP 0x62 +#define SAA7127_REG_SUBC3 0x63 +#define SAA7127_REG_SUBC2 0x64 +#define SAA7127_REG_SUBC1 0x65 +#define SAA7127_REG_SUBC0 0x66 +#define SAA7127_REG_LINE_21_ODD_0 0x67 +#define SAA7127_REG_LINE_21_ODD_1 0x68 +#define SAA7127_REG_LINE_21_EVEN_0 0x69 +#define SAA7127_REG_LINE_21_EVEN_1 0x6a +#define SAA7127_REG_RCV_PORT_CONTROL 0x6b +#define SAA7127_REG_VTRIG 0x6c +#define SAA7127_REG_HTRIG_HI 0x6d +#define SAA7127_REG_MULTI 0x6e +#define SAA7127_REG_CLOSED_CAPTION 0x6f +#define SAA7127_REG_RCV2_OUTPUT_START 0x70 +#define SAA7127_REG_RCV2_OUTPUT_END 0x71 +#define SAA7127_REG_RCV2_OUTPUT_MSBS 0x72 +#define SAA7127_REG_TTX_REQUEST_H_START 0x73 +#define SAA7127_REG_TTX_REQUEST_H_DELAY_LENGTH 0x74 +#define SAA7127_REG_CSYNC_ADVANCE_VSYNC_SHIFT 0x75 +#define SAA7127_REG_TTX_ODD_REQ_VERT_START 0x76 +#define SAA7127_REG_TTX_ODD_REQ_VERT_END 0x77 +#define SAA7127_REG_TTX_EVEN_REQ_VERT_START 0x78 +#define SAA7127_REG_TTX_EVEN_REQ_VERT_END 0x79 +#define SAA7127_REG_FIRST_ACTIVE 0x7a +#define SAA7127_REG_LAST_ACTIVE 0x7b +#define SAA7127_REG_MSB_VERTICAL 0x7c +#define SAA7127_REG_DISABLE_TTX_LINE_LO_0 0x7e +#define SAA7127_REG_DISABLE_TTX_LINE_LO_1 0x7f + +/* + ********************************************************************** + * + * Arrays with configuration parameters for the SAA7127 + * + ********************************************************************** + */ + +struct i2c_reg_value { + unsigned char reg; + unsigned char value; +}; + +static const struct i2c_reg_value saa7129_init_config_extra[] = { + { SAA7127_REG_OUTPUT_PORT_CONTROL, 0x38 }, + { SAA7127_REG_VTRIG, 0xfa }, + { 0, 0 } +}; + +static const struct i2c_reg_value saa7127_init_config_common[] = { + { SAA7127_REG_WIDESCREEN_CONFIG, 0x0d }, + { SAA7127_REG_WIDESCREEN_ENABLE, 0x00 }, + { SAA7127_REG_COPYGEN_0, 0x77 }, + { SAA7127_REG_COPYGEN_1, 0x41 }, + { SAA7127_REG_COPYGEN_2, 0x00 }, /* Macrovision enable/disable */ + { SAA7127_REG_OUTPUT_PORT_CONTROL, 0xbf }, + { SAA7127_REG_GAIN_LUMINANCE_RGB, 0x00 }, + { SAA7127_REG_GAIN_COLORDIFF_RGB, 0x00 }, + { SAA7127_REG_INPUT_PORT_CONTROL_1, 0x80 }, /* for color bars */ + { SAA7127_REG_LINE_21_ODD_0, 0x77 }, + { SAA7127_REG_LINE_21_ODD_1, 0x41 }, + { SAA7127_REG_LINE_21_EVEN_0, 0x88 }, + { SAA7127_REG_LINE_21_EVEN_1, 0x41 }, + { SAA7127_REG_RCV_PORT_CONTROL, 0x12 }, + { SAA7127_REG_VTRIG, 0xf9 }, + { SAA7127_REG_HTRIG_HI, 0x00 }, + { SAA7127_REG_RCV2_OUTPUT_START, 0x41 }, + { SAA7127_REG_RCV2_OUTPUT_END, 0xc3 }, + { SAA7127_REG_RCV2_OUTPUT_MSBS, 0x00 }, + { SAA7127_REG_TTX_REQUEST_H_START, 0x3e }, + { SAA7127_REG_TTX_REQUEST_H_DELAY_LENGTH, 0xb8 }, + { SAA7127_REG_CSYNC_ADVANCE_VSYNC_SHIFT, 0x03 }, + { SAA7127_REG_TTX_ODD_REQ_VERT_START, 0x15 }, + { SAA7127_REG_TTX_ODD_REQ_VERT_END, 0x16 }, + { SAA7127_REG_TTX_EVEN_REQ_VERT_START, 0x15 }, + { SAA7127_REG_TTX_EVEN_REQ_VERT_END, 0x16 }, + { SAA7127_REG_FIRST_ACTIVE, 0x1a }, + { SAA7127_REG_LAST_ACTIVE, 0x01 }, + { SAA7127_REG_MSB_VERTICAL, 0xc0 }, + { SAA7127_REG_DISABLE_TTX_LINE_LO_0, 0x00 }, + { SAA7127_REG_DISABLE_TTX_LINE_LO_1, 0x00 }, + { 0, 0 } +}; + +#define SAA7127_60HZ_DAC_CONTROL 0x15 +static const struct i2c_reg_value saa7127_init_config_60hz[] = { + { SAA7127_REG_BURST_START, 0x19 }, + /* BURST_END is also used as a chip ID in saa7127_probe */ + { SAA7127_REG_BURST_END, 0x1d }, + { SAA7127_REG_CHROMA_PHASE, 0xa3 }, + { SAA7127_REG_GAINU, 0x98 }, + { SAA7127_REG_GAINV, 0xd3 }, + { SAA7127_REG_BLACK_LEVEL, 0x39 }, + { SAA7127_REG_BLANKING_LEVEL, 0x2e }, + { SAA7127_REG_VBI_BLANKING, 0x2e }, + { SAA7127_REG_DAC_CONTROL, 0x15 }, + { SAA7127_REG_BURST_AMP, 0x4d }, + { SAA7127_REG_SUBC3, 0x1f }, + { SAA7127_REG_SUBC2, 0x7c }, + { SAA7127_REG_SUBC1, 0xf0 }, + { SAA7127_REG_SUBC0, 0x21 }, + { SAA7127_REG_MULTI, 0x90 }, + { SAA7127_REG_CLOSED_CAPTION, 0x11 }, + { 0, 0 } +}; + +#define SAA7127_50HZ_PAL_DAC_CONTROL 0x02 +static struct i2c_reg_value saa7127_init_config_50hz_pal[] = { + { SAA7127_REG_BURST_START, 0x21 }, + /* BURST_END is also used as a chip ID in saa7127_probe */ + { SAA7127_REG_BURST_END, 0x1d }, + { SAA7127_REG_CHROMA_PHASE, 0x3f }, + { SAA7127_REG_GAINU, 0x7d }, + { SAA7127_REG_GAINV, 0xaf }, + { SAA7127_REG_BLACK_LEVEL, 0x33 }, + { SAA7127_REG_BLANKING_LEVEL, 0x35 }, + { SAA7127_REG_VBI_BLANKING, 0x35 }, + { SAA7127_REG_DAC_CONTROL, 0x02 }, + { SAA7127_REG_BURST_AMP, 0x2f }, + { SAA7127_REG_SUBC3, 0xcb }, + { SAA7127_REG_SUBC2, 0x8a }, + { SAA7127_REG_SUBC1, 0x09 }, + { SAA7127_REG_SUBC0, 0x2a }, + { SAA7127_REG_MULTI, 0xa0 }, + { SAA7127_REG_CLOSED_CAPTION, 0x00 }, + { 0, 0 } +}; + +#define SAA7127_50HZ_SECAM_DAC_CONTROL 0x08 +static struct i2c_reg_value saa7127_init_config_50hz_secam[] = { + { SAA7127_REG_BURST_START, 0x21 }, + /* BURST_END is also used as a chip ID in saa7127_probe */ + { SAA7127_REG_BURST_END, 0x1d }, + { SAA7127_REG_CHROMA_PHASE, 0x3f }, + { SAA7127_REG_GAINU, 0x6a }, + { SAA7127_REG_GAINV, 0x81 }, + { SAA7127_REG_BLACK_LEVEL, 0x33 }, + { SAA7127_REG_BLANKING_LEVEL, 0x35 }, + { SAA7127_REG_VBI_BLANKING, 0x35 }, + { SAA7127_REG_DAC_CONTROL, 0x08 }, + { SAA7127_REG_BURST_AMP, 0x2f }, + { SAA7127_REG_SUBC3, 0xb2 }, + { SAA7127_REG_SUBC2, 0x3b }, + { SAA7127_REG_SUBC1, 0xa3 }, + { SAA7127_REG_SUBC0, 0x28 }, + { SAA7127_REG_MULTI, 0x90 }, + { SAA7127_REG_CLOSED_CAPTION, 0x00 }, + { 0, 0 } +}; + +/* + ********************************************************************** + * + * Encoder Struct, holds the configuration state of the encoder + * + ********************************************************************** + */ + +struct saa7127_state { + struct v4l2_subdev sd; + v4l2_std_id std; + u32 ident; + enum saa7127_input_type input_type; + enum saa7127_output_type output_type; + int video_enable; + int wss_enable; + u16 wss_mode; + int cc_enable; + u16 cc_data; + int xds_enable; + u16 xds_data; + int vps_enable; + u8 vps_data[5]; + u8 reg_2d; + u8 reg_3a; + u8 reg_3a_cb; /* colorbar bit */ + u8 reg_61; +}; + +static inline struct saa7127_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa7127_state, sd); +} + +static const char * const output_strs[] = +{ + "S-Video + Composite", + "Composite", + "S-Video", + "RGB", + "YUV C", + "YUV V" +}; + +static const char * const wss_strs[] = { + "invalid", + "letterbox 14:9 center", + "letterbox 14:9 top", + "invalid", + "letterbox 16:9 top", + "invalid", + "invalid", + "16:9 full format anamorphic", + "4:3 full format", + "invalid", + "invalid", + "letterbox 16:9 center", + "invalid", + "letterbox >16:9 center", + "14:9 full format center", + "invalid", +}; + +/* ----------------------------------------------------------------------- */ + +static int saa7127_read(struct v4l2_subdev *sd, u8 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_read_byte_data(client, reg); +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int i; + + for (i = 0; i < 3; i++) { + if (i2c_smbus_write_byte_data(client, reg, val) == 0) + return 0; + } + v4l2_err(sd, "I2C Write Problem\n"); + return -1; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_write_inittab(struct v4l2_subdev *sd, + const struct i2c_reg_value *regs) +{ + while (regs->reg != 0) { + saa7127_write(sd, regs->reg, regs->value); + regs++; + } + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_vps(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) +{ + struct saa7127_state *state = to_state(sd); + int enable = (data->line != 0); + + if (enable && (data->field != 0 || data->line != 16)) + return -EINVAL; + if (state->vps_enable != enable) { + v4l2_dbg(1, debug, sd, "Turn VPS Signal %s\n", enable ? "on" : "off"); + saa7127_write(sd, 0x54, enable << 7); + state->vps_enable = enable; + } + if (!enable) + return 0; + + state->vps_data[0] = data->data[2]; + state->vps_data[1] = data->data[8]; + state->vps_data[2] = data->data[9]; + state->vps_data[3] = data->data[10]; + state->vps_data[4] = data->data[11]; + v4l2_dbg(1, debug, sd, "Set VPS data %*ph\n", 5, state->vps_data); + saa7127_write(sd, 0x55, state->vps_data[0]); + saa7127_write(sd, 0x56, state->vps_data[1]); + saa7127_write(sd, 0x57, state->vps_data[2]); + saa7127_write(sd, 0x58, state->vps_data[3]); + saa7127_write(sd, 0x59, state->vps_data[4]); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_cc(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) +{ + struct saa7127_state *state = to_state(sd); + u16 cc = data->data[1] << 8 | data->data[0]; + int enable = (data->line != 0); + + if (enable && (data->field != 0 || data->line != 21)) + return -EINVAL; + if (state->cc_enable != enable) { + v4l2_dbg(1, debug, sd, + "Turn CC %s\n", enable ? "on" : "off"); + saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION, + (state->xds_enable << 7) | (enable << 6) | 0x11); + state->cc_enable = enable; + } + if (!enable) + return 0; + + v4l2_dbg(2, debug, sd, "CC data: %04x\n", cc); + saa7127_write(sd, SAA7127_REG_LINE_21_ODD_0, cc & 0xff); + saa7127_write(sd, SAA7127_REG_LINE_21_ODD_1, cc >> 8); + state->cc_data = cc; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_xds(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) +{ + struct saa7127_state *state = to_state(sd); + u16 xds = data->data[1] << 8 | data->data[0]; + int enable = (data->line != 0); + + if (enable && (data->field != 1 || data->line != 21)) + return -EINVAL; + if (state->xds_enable != enable) { + v4l2_dbg(1, debug, sd, "Turn XDS %s\n", enable ? "on" : "off"); + saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION, + (enable << 7) | (state->cc_enable << 6) | 0x11); + state->xds_enable = enable; + } + if (!enable) + return 0; + + v4l2_dbg(2, debug, sd, "XDS data: %04x\n", xds); + saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_0, xds & 0xff); + saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_1, xds >> 8); + state->xds_data = xds; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_wss(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) +{ + struct saa7127_state *state = to_state(sd); + int enable = (data->line != 0); + + if (enable && (data->field != 0 || data->line != 23)) + return -EINVAL; + if (state->wss_enable != enable) { + v4l2_dbg(1, debug, sd, "Turn WSS %s\n", enable ? "on" : "off"); + saa7127_write(sd, 0x27, enable << 7); + state->wss_enable = enable; + } + if (!enable) + return 0; + + saa7127_write(sd, 0x26, data->data[0]); + saa7127_write(sd, 0x27, 0x80 | (data->data[1] & 0x3f)); + v4l2_dbg(1, debug, sd, + "WSS mode: %s\n", wss_strs[data->data[0] & 0xf]); + state->wss_mode = (data->data[1] & 0x3f) << 8 | data->data[0]; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_video_enable(struct v4l2_subdev *sd, int enable) +{ + struct saa7127_state *state = to_state(sd); + + if (enable) { + v4l2_dbg(1, debug, sd, "Enable Video Output\n"); + saa7127_write(sd, 0x2d, state->reg_2d); + saa7127_write(sd, 0x61, state->reg_61); + } else { + v4l2_dbg(1, debug, sd, "Disable Video Output\n"); + saa7127_write(sd, 0x2d, (state->reg_2d & 0xf0)); + saa7127_write(sd, 0x61, (state->reg_61 | 0xc0)); + } + state->video_enable = enable; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct saa7127_state *state = to_state(sd); + const struct i2c_reg_value *inittab; + + if (std & V4L2_STD_525_60) { + v4l2_dbg(1, debug, sd, "Selecting 60 Hz video Standard\n"); + inittab = saa7127_init_config_60hz; + state->reg_61 = SAA7127_60HZ_DAC_CONTROL; + + } else if (state->ident == V4L2_IDENT_SAA7129 && + (std & V4L2_STD_SECAM) && + !(std & (V4L2_STD_625_50 & ~V4L2_STD_SECAM))) { + + /* If and only if SECAM, with a SAA712[89] */ + v4l2_dbg(1, debug, sd, + "Selecting 50 Hz SECAM video Standard\n"); + inittab = saa7127_init_config_50hz_secam; + state->reg_61 = SAA7127_50HZ_SECAM_DAC_CONTROL; + + } else { + v4l2_dbg(1, debug, sd, "Selecting 50 Hz PAL video Standard\n"); + inittab = saa7127_init_config_50hz_pal; + state->reg_61 = SAA7127_50HZ_PAL_DAC_CONTROL; + } + + /* Write Table */ + saa7127_write_inittab(sd, inittab); + state->std = std; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_output_type(struct v4l2_subdev *sd, int output) +{ + struct saa7127_state *state = to_state(sd); + + switch (output) { + case SAA7127_OUTPUT_TYPE_RGB: + state->reg_2d = 0x0f; /* RGB + CVBS (for sync) */ + state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ + break; + + case SAA7127_OUTPUT_TYPE_COMPOSITE: + if (state->ident == V4L2_IDENT_SAA7129) + state->reg_2d = 0x20; /* CVBS only */ + else + state->reg_2d = 0x08; /* 00001000 CVBS only, RGB DAC's off (high impedance mode) */ + state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ + break; + + case SAA7127_OUTPUT_TYPE_SVIDEO: + if (state->ident == V4L2_IDENT_SAA7129) + state->reg_2d = 0x18; /* Y + C */ + else + state->reg_2d = 0xff; /*11111111 croma -> R, luma -> CVBS + G + B */ + state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ + break; + + case SAA7127_OUTPUT_TYPE_YUV_V: + state->reg_2d = 0x4f; /* reg 2D = 01001111, all DAC's on, RGB + VBS */ + state->reg_3a = 0x0b; /* reg 3A = 00001011, bypass RGB-matrix */ + break; + + case SAA7127_OUTPUT_TYPE_YUV_C: + state->reg_2d = 0x0f; /* reg 2D = 00001111, all DAC's on, RGB + CVBS */ + state->reg_3a = 0x0b; /* reg 3A = 00001011, bypass RGB-matrix */ + break; + + case SAA7127_OUTPUT_TYPE_BOTH: + if (state->ident == V4L2_IDENT_SAA7129) + state->reg_2d = 0x38; + else + state->reg_2d = 0xbf; + state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ + break; + + default: + return -EINVAL; + } + v4l2_dbg(1, debug, sd, + "Selecting %s output type\n", output_strs[output]); + + /* Configure Encoder */ + saa7127_write(sd, 0x2d, state->reg_2d); + saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb); + state->output_type = output; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_input_type(struct v4l2_subdev *sd, int input) +{ + struct saa7127_state *state = to_state(sd); + + switch (input) { + case SAA7127_INPUT_TYPE_NORMAL: /* avia */ + v4l2_dbg(1, debug, sd, "Selecting Normal Encoder Input\n"); + state->reg_3a_cb = 0; + break; + + case SAA7127_INPUT_TYPE_TEST_IMAGE: /* color bar */ + v4l2_dbg(1, debug, sd, "Selecting Color Bar generator\n"); + state->reg_3a_cb = 0x80; + break; + + default: + return -EINVAL; + } + saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb); + state->input_type = input; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct saa7127_state *state = to_state(sd); + + if (state->std == std) + return 0; + return saa7127_set_std(sd, std); +} + +static int saa7127_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct saa7127_state *state = to_state(sd); + int rc = 0; + + if (state->input_type != input) + rc = saa7127_set_input_type(sd, input); + if (rc == 0 && state->output_type != output) + rc = saa7127_set_output_type(sd, output); + return rc; +} + +static int saa7127_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct saa7127_state *state = to_state(sd); + + if (state->video_enable == enable) + return 0; + return saa7127_set_video_enable(sd, enable); +} + +static int saa7127_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt) +{ + struct saa7127_state *state = to_state(sd); + + memset(fmt, 0, sizeof(*fmt)); + if (state->vps_enable) + fmt->service_lines[0][16] = V4L2_SLICED_VPS; + if (state->wss_enable) + fmt->service_lines[0][23] = V4L2_SLICED_WSS_625; + if (state->cc_enable) { + fmt->service_lines[0][21] = V4L2_SLICED_CAPTION_525; + fmt->service_lines[1][21] = V4L2_SLICED_CAPTION_525; + } + fmt->service_set = + (state->vps_enable ? V4L2_SLICED_VPS : 0) | + (state->wss_enable ? V4L2_SLICED_WSS_625 : 0) | + (state->cc_enable ? V4L2_SLICED_CAPTION_525 : 0); + return 0; +} + +static int saa7127_s_vbi_data(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) +{ + switch (data->id) { + case V4L2_SLICED_WSS_625: + return saa7127_set_wss(sd, data); + case V4L2_SLICED_VPS: + return saa7127_set_vps(sd, data); + case V4L2_SLICED_CAPTION_525: + if (data->field == 0) + return saa7127_set_cc(sd, data); + return saa7127_set_xds(sd, data); + default: + return -EINVAL; + } + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int saa7127_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = saa7127_read(sd, reg->reg & 0xff); + reg->size = 1; + return 0; +} + +static int saa7127_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + saa7127_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif + +static int saa7127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct saa7127_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0); +} + +static int saa7127_log_status(struct v4l2_subdev *sd) +{ + struct saa7127_state *state = to_state(sd); + + v4l2_info(sd, "Standard: %s\n", (state->std & V4L2_STD_525_60) ? "60 Hz" : "50 Hz"); + v4l2_info(sd, "Input: %s\n", state->input_type ? "color bars" : "normal"); + v4l2_info(sd, "Output: %s\n", state->video_enable ? + output_strs[state->output_type] : "disabled"); + v4l2_info(sd, "WSS: %s\n", state->wss_enable ? + wss_strs[state->wss_mode] : "disabled"); + v4l2_info(sd, "VPS: %s\n", state->vps_enable ? "enabled" : "disabled"); + v4l2_info(sd, "CC: %s\n", state->cc_enable ? "enabled" : "disabled"); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops saa7127_core_ops = { + .log_status = saa7127_log_status, + .g_chip_ident = saa7127_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = saa7127_g_register, + .s_register = saa7127_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops saa7127_video_ops = { + .s_std_output = saa7127_s_std_output, + .s_routing = saa7127_s_routing, + .s_stream = saa7127_s_stream, +}; + +static const struct v4l2_subdev_vbi_ops saa7127_vbi_ops = { + .s_vbi_data = saa7127_s_vbi_data, + .g_sliced_fmt = saa7127_g_sliced_fmt, +}; + +static const struct v4l2_subdev_ops saa7127_ops = { + .core = &saa7127_core_ops, + .video = &saa7127_video_ops, + .vbi = &saa7127_vbi_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static int saa7127_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct saa7127_state *state; + struct v4l2_subdev *sd; + struct v4l2_sliced_vbi_data vbi = { 0, 0, 0, 0 }; /* set to disabled */ + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_dbg(1, debug, client, "detecting saa7127 client on address 0x%x\n", + client->addr << 1); + + state = kzalloc(sizeof(struct saa7127_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &saa7127_ops); + + /* First test register 0: Bits 5-7 are a version ID (should be 0), + and bit 2 should also be 0. + This is rather general, so the second test is more specific and + looks at the 'ending point of burst in clock cycles' which is + 0x1d after a reset and not expected to ever change. */ + if ((saa7127_read(sd, 0) & 0xe4) != 0 || + (saa7127_read(sd, 0x29) & 0x3f) != 0x1d) { + v4l2_dbg(1, debug, sd, "saa7127 not found\n"); + kfree(state); + return -ENODEV; + } + + if (id->driver_data) { /* Chip type is already known */ + state->ident = id->driver_data; + } else { /* Needs detection */ + int read_result; + + /* Detect if it's an saa7129 */ + read_result = saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2); + saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2, 0xaa); + if (saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2) == 0xaa) { + saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2, + read_result); + state->ident = V4L2_IDENT_SAA7129; + strlcpy(client->name, "saa7129", I2C_NAME_SIZE); + } else { + state->ident = V4L2_IDENT_SAA7127; + strlcpy(client->name, "saa7127", I2C_NAME_SIZE); + } + } + + v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, + client->addr << 1, client->adapter->name); + + v4l2_dbg(1, debug, sd, "Configuring encoder\n"); + saa7127_write_inittab(sd, saa7127_init_config_common); + saa7127_set_std(sd, V4L2_STD_NTSC); + saa7127_set_output_type(sd, SAA7127_OUTPUT_TYPE_BOTH); + saa7127_set_vps(sd, &vbi); + saa7127_set_wss(sd, &vbi); + saa7127_set_cc(sd, &vbi); + saa7127_set_xds(sd, &vbi); + if (test_image == 1) + /* The Encoder has an internal Colorbar generator */ + /* This can be used for debugging */ + saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_TEST_IMAGE); + else + saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_NORMAL); + saa7127_set_video_enable(sd, 1); + + if (state->ident == V4L2_IDENT_SAA7129) + saa7127_write_inittab(sd, saa7129_init_config_extra); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + /* Turn off TV output */ + saa7127_set_video_enable(sd, 0); + kfree(to_state(sd)); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_device_id saa7127_id[] = { + { "saa7127_auto", 0 }, /* auto-detection */ + { "saa7126", V4L2_IDENT_SAA7127 }, + { "saa7127", V4L2_IDENT_SAA7127 }, + { "saa7128", V4L2_IDENT_SAA7129 }, + { "saa7129", V4L2_IDENT_SAA7129 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa7127_id); + +static struct i2c_driver saa7127_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "saa7127", + }, + .probe = saa7127_probe, + .remove = saa7127_remove, + .id_table = saa7127_id, +}; + +module_i2c_driver(saa7127_driver); diff --git a/drivers/media/i2c/saa717x.c b/drivers/media/i2c/saa717x.c new file mode 100644 index 00000000000..1e84466515a --- /dev/null +++ b/drivers/media/i2c/saa717x.c @@ -0,0 +1,1378 @@ +/* + * saa717x - Philips SAA717xHL video decoder driver + * + * Based on the saa7115 driver + * + * Changes by Ohta Kyuma + * - Apply to SAA717x,NEC uPD64031,uPD64083. (1/31/2004) + * + * Changes by T.Adachi (tadachi@tadachi-net.com) + * - support audio, video scaler etc, and checked the initialize sequence. + * + * Cleaned up by Hans Verkuil + * + * Note: this is a reversed engineered driver based on captures from + * the I2C bus under Windows. This chip is very similar to the saa7134, + * though. Unfortunately, this driver is currently only working for NTSC. + * + * 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 +#include +#include +#include + +#include +#include +#include +#include + +MODULE_DESCRIPTION("Philips SAA717x audio/video decoder driver"); +MODULE_AUTHOR("K. Ohta, T. Adachi, Hans Verkuil"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ + +struct saa717x_state { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + v4l2_std_id std; + int input; + int enable; + int radio; + int playback; + int audio; + int tuner_audio_mode; + int audio_main_mute; + int audio_main_vol_r; + int audio_main_vol_l; + u16 audio_main_bass; + u16 audio_main_treble; + u16 audio_main_volume; + u16 audio_main_balance; + int audio_input; +}; + +static inline struct saa717x_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa717x_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct saa717x_state, hdl)->sd; +} + +/* ----------------------------------------------------------------------- */ + +/* for audio mode */ +#define TUNER_AUDIO_MONO 0 /* LL */ +#define TUNER_AUDIO_STEREO 1 /* LR */ +#define TUNER_AUDIO_LANG1 2 /* LL */ +#define TUNER_AUDIO_LANG2 3 /* RR */ + +#define SAA717X_NTSC_WIDTH (704) +#define SAA717X_NTSC_HEIGHT (480) + +/* ----------------------------------------------------------------------- */ + +static int saa717x_write(struct v4l2_subdev *sd, u32 reg, u32 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct i2c_adapter *adap = client->adapter; + int fw_addr = reg == 0x454 || (reg >= 0x464 && reg <= 0x478) || reg == 0x480 || reg == 0x488; + unsigned char mm1[6]; + struct i2c_msg msg; + + msg.flags = 0; + msg.addr = client->addr; + mm1[0] = (reg >> 8) & 0xff; + mm1[1] = reg & 0xff; + + if (fw_addr) { + mm1[4] = (value >> 16) & 0xff; + mm1[3] = (value >> 8) & 0xff; + mm1[2] = value & 0xff; + } else { + mm1[2] = value & 0xff; + } + msg.len = fw_addr ? 5 : 3; /* Long Registers have *only* three bytes! */ + msg.buf = mm1; + v4l2_dbg(2, debug, sd, "wrote: reg 0x%03x=%08x\n", reg, value); + return i2c_transfer(adap, &msg, 1) == 1; +} + +static void saa717x_write_regs(struct v4l2_subdev *sd, u32 *data) +{ + while (data[0] || data[1]) { + saa717x_write(sd, data[0], data[1]); + data += 2; + } +} + +static u32 saa717x_read(struct v4l2_subdev *sd, u32 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct i2c_adapter *adap = client->adapter; + int fw_addr = (reg >= 0x404 && reg <= 0x4b8) || reg == 0x528; + unsigned char mm1[2]; + unsigned char mm2[4] = { 0, 0, 0, 0 }; + struct i2c_msg msgs[2]; + u32 value; + + msgs[0].flags = 0; + msgs[1].flags = I2C_M_RD; + msgs[0].addr = msgs[1].addr = client->addr; + mm1[0] = (reg >> 8) & 0xff; + mm1[1] = reg & 0xff; + msgs[0].len = 2; + msgs[0].buf = mm1; + msgs[1].len = fw_addr ? 3 : 1; /* Multibyte Registers contains *only* 3 bytes */ + msgs[1].buf = mm2; + i2c_transfer(adap, msgs, 2); + + if (fw_addr) + value = (mm2[2] & 0xff) | ((mm2[1] & 0xff) >> 8) | ((mm2[0] & 0xff) >> 16); + else + value = mm2[0] & 0xff; + + v4l2_dbg(2, debug, sd, "read: reg 0x%03x=0x%08x\n", reg, value); + return value; +} + +/* ----------------------------------------------------------------------- */ + +static u32 reg_init_initialize[] = +{ + /* from linux driver */ + 0x101, 0x008, /* Increment delay */ + + 0x103, 0x000, /* Analog input control 2 */ + 0x104, 0x090, /* Analog input control 3 */ + 0x105, 0x090, /* Analog input control 4 */ + 0x106, 0x0eb, /* Horizontal sync start */ + 0x107, 0x0e0, /* Horizontal sync stop */ + 0x109, 0x055, /* Luminance control */ + + 0x10f, 0x02a, /* Chroma gain control */ + 0x110, 0x000, /* Chroma control 2 */ + + 0x114, 0x045, /* analog/ADC */ + + 0x118, 0x040, /* RAW data gain */ + 0x119, 0x080, /* RAW data offset */ + + 0x044, 0x000, /* VBI horizontal input window start (L) TASK A */ + 0x045, 0x000, /* VBI horizontal input window start (H) TASK A */ + 0x046, 0x0cf, /* VBI horizontal input window stop (L) TASK A */ + 0x047, 0x002, /* VBI horizontal input window stop (H) TASK A */ + + 0x049, 0x000, /* VBI vertical input window start (H) TASK A */ + + 0x04c, 0x0d0, /* VBI horizontal output length (L) TASK A */ + 0x04d, 0x002, /* VBI horizontal output length (H) TASK A */ + + 0x064, 0x080, /* Lumina brightness TASK A */ + 0x065, 0x040, /* Luminance contrast TASK A */ + 0x066, 0x040, /* Chroma saturation TASK A */ + /* 067H: Reserved */ + 0x068, 0x000, /* VBI horizontal scaling increment (L) TASK A */ + 0x069, 0x004, /* VBI horizontal scaling increment (H) TASK A */ + 0x06a, 0x000, /* VBI phase offset TASK A */ + + 0x06e, 0x000, /* Horizontal phase offset Luma TASK A */ + 0x06f, 0x000, /* Horizontal phase offset Chroma TASK A */ + + 0x072, 0x000, /* Vertical filter mode TASK A */ + + 0x084, 0x000, /* VBI horizontal input window start (L) TAKS B */ + 0x085, 0x000, /* VBI horizontal input window start (H) TAKS B */ + 0x086, 0x0cf, /* VBI horizontal input window stop (L) TAKS B */ + 0x087, 0x002, /* VBI horizontal input window stop (H) TAKS B */ + + 0x089, 0x000, /* VBI vertical input window start (H) TAKS B */ + + 0x08c, 0x0d0, /* VBI horizontal output length (L) TASK B */ + 0x08d, 0x002, /* VBI horizontal output length (H) TASK B */ + + 0x0a4, 0x080, /* Lumina brightness TASK B */ + 0x0a5, 0x040, /* Luminance contrast TASK B */ + 0x0a6, 0x040, /* Chroma saturation TASK B */ + /* 0A7H reserved */ + 0x0a8, 0x000, /* VBI horizontal scaling increment (L) TASK B */ + 0x0a9, 0x004, /* VBI horizontal scaling increment (H) TASK B */ + 0x0aa, 0x000, /* VBI phase offset TASK B */ + + 0x0ae, 0x000, /* Horizontal phase offset Luma TASK B */ + 0x0af, 0x000, /*Horizontal phase offset Chroma TASK B */ + + 0x0b2, 0x000, /* Vertical filter mode TASK B */ + + 0x00c, 0x000, /* Start point GREEN path */ + 0x00d, 0x000, /* Start point BLUE path */ + 0x00e, 0x000, /* Start point RED path */ + + 0x010, 0x010, /* GREEN path gamma curve --- */ + 0x011, 0x020, + 0x012, 0x030, + 0x013, 0x040, + 0x014, 0x050, + 0x015, 0x060, + 0x016, 0x070, + 0x017, 0x080, + 0x018, 0x090, + 0x019, 0x0a0, + 0x01a, 0x0b0, + 0x01b, 0x0c0, + 0x01c, 0x0d0, + 0x01d, 0x0e0, + 0x01e, 0x0f0, + 0x01f, 0x0ff, /* --- GREEN path gamma curve */ + + 0x020, 0x010, /* BLUE path gamma curve --- */ + 0x021, 0x020, + 0x022, 0x030, + 0x023, 0x040, + 0x024, 0x050, + 0x025, 0x060, + 0x026, 0x070, + 0x027, 0x080, + 0x028, 0x090, + 0x029, 0x0a0, + 0x02a, 0x0b0, + 0x02b, 0x0c0, + 0x02c, 0x0d0, + 0x02d, 0x0e0, + 0x02e, 0x0f0, + 0x02f, 0x0ff, /* --- BLUE path gamma curve */ + + 0x030, 0x010, /* RED path gamma curve --- */ + 0x031, 0x020, + 0x032, 0x030, + 0x033, 0x040, + 0x034, 0x050, + 0x035, 0x060, + 0x036, 0x070, + 0x037, 0x080, + 0x038, 0x090, + 0x039, 0x0a0, + 0x03a, 0x0b0, + 0x03b, 0x0c0, + 0x03c, 0x0d0, + 0x03d, 0x0e0, + 0x03e, 0x0f0, + 0x03f, 0x0ff, /* --- RED path gamma curve */ + + 0x109, 0x085, /* Luminance control */ + + /**** from app start ****/ + 0x584, 0x000, /* AGC gain control */ + 0x585, 0x000, /* Program count */ + 0x586, 0x003, /* Status reset */ + 0x588, 0x0ff, /* Number of audio samples (L) */ + 0x589, 0x00f, /* Number of audio samples (M) */ + 0x58a, 0x000, /* Number of audio samples (H) */ + 0x58b, 0x000, /* Audio select */ + 0x58c, 0x010, /* Audio channel assign1 */ + 0x58d, 0x032, /* Audio channel assign2 */ + 0x58e, 0x054, /* Audio channel assign3 */ + 0x58f, 0x023, /* Audio format */ + 0x590, 0x000, /* SIF control */ + + 0x595, 0x000, /* ?? */ + 0x596, 0x000, /* ?? */ + 0x597, 0x000, /* ?? */ + + 0x464, 0x00, /* Digital input crossbar1 */ + + 0x46c, 0xbbbb10, /* Digital output selection1-3 */ + 0x470, 0x101010, /* Digital output selection4-6 */ + + 0x478, 0x00, /* Sound feature control */ + + 0x474, 0x18, /* Softmute control */ + + 0x454, 0x0425b9, /* Sound Easy programming(reset) */ + 0x454, 0x042539, /* Sound Easy programming(reset) */ + + + /**** common setting( of DVD play, including scaler commands) ****/ + 0x042, 0x003, /* Data path configuration for VBI (TASK A) */ + + 0x082, 0x003, /* Data path configuration for VBI (TASK B) */ + + 0x108, 0x0f8, /* Sync control */ + 0x2a9, 0x0fd, /* ??? */ + 0x102, 0x089, /* select video input "mode 9" */ + 0x111, 0x000, /* Mode/delay control */ + + 0x10e, 0x00a, /* Chroma control 1 */ + + 0x594, 0x002, /* SIF, analog I/O select */ + + 0x454, 0x0425b9, /* Sound */ + 0x454, 0x042539, + + 0x111, 0x000, + 0x10e, 0x00a, + 0x464, 0x000, + 0x300, 0x000, + 0x301, 0x006, + 0x302, 0x000, + 0x303, 0x006, + 0x308, 0x040, + 0x309, 0x000, + 0x30a, 0x000, + 0x30b, 0x000, + 0x000, 0x002, + 0x001, 0x000, + 0x002, 0x000, + 0x003, 0x000, + 0x004, 0x033, + 0x040, 0x01d, + 0x041, 0x001, + 0x042, 0x004, + 0x043, 0x000, + 0x080, 0x01e, + 0x081, 0x001, + 0x082, 0x004, + 0x083, 0x000, + 0x190, 0x018, + 0x115, 0x000, + 0x116, 0x012, + 0x117, 0x018, + 0x04a, 0x011, + 0x08a, 0x011, + 0x04b, 0x000, + 0x08b, 0x000, + 0x048, 0x000, + 0x088, 0x000, + 0x04e, 0x012, + 0x08e, 0x012, + 0x058, 0x012, + 0x098, 0x012, + 0x059, 0x000, + 0x099, 0x000, + 0x05a, 0x003, + 0x09a, 0x003, + 0x05b, 0x001, + 0x09b, 0x001, + 0x054, 0x008, + 0x094, 0x008, + 0x055, 0x000, + 0x095, 0x000, + 0x056, 0x0c7, + 0x096, 0x0c7, + 0x057, 0x002, + 0x097, 0x002, + 0x0ff, 0x0ff, + 0x060, 0x001, + 0x0a0, 0x001, + 0x061, 0x000, + 0x0a1, 0x000, + 0x062, 0x000, + 0x0a2, 0x000, + 0x063, 0x000, + 0x0a3, 0x000, + 0x070, 0x000, + 0x0b0, 0x000, + 0x071, 0x004, + 0x0b1, 0x004, + 0x06c, 0x0e9, + 0x0ac, 0x0e9, + 0x06d, 0x003, + 0x0ad, 0x003, + 0x05c, 0x0d0, + 0x09c, 0x0d0, + 0x05d, 0x002, + 0x09d, 0x002, + 0x05e, 0x0f2, + 0x09e, 0x0f2, + 0x05f, 0x000, + 0x09f, 0x000, + 0x074, 0x000, + 0x0b4, 0x000, + 0x075, 0x000, + 0x0b5, 0x000, + 0x076, 0x000, + 0x0b6, 0x000, + 0x077, 0x000, + 0x0b7, 0x000, + 0x195, 0x008, + 0x0ff, 0x0ff, + 0x108, 0x0f8, + 0x111, 0x000, + 0x10e, 0x00a, + 0x2a9, 0x0fd, + 0x464, 0x001, + 0x454, 0x042135, + 0x598, 0x0e7, + 0x599, 0x07d, + 0x59a, 0x018, + 0x59c, 0x066, + 0x59d, 0x090, + 0x59e, 0x001, + 0x584, 0x000, + 0x585, 0x000, + 0x586, 0x003, + 0x588, 0x0ff, + 0x589, 0x00f, + 0x58a, 0x000, + 0x58b, 0x000, + 0x58c, 0x010, + 0x58d, 0x032, + 0x58e, 0x054, + 0x58f, 0x023, + 0x590, 0x000, + 0x595, 0x000, + 0x596, 0x000, + 0x597, 0x000, + 0x464, 0x000, + 0x46c, 0xbbbb10, + 0x470, 0x101010, + + + 0x478, 0x000, + 0x474, 0x018, + 0x454, 0x042135, + 0x598, 0x0e7, + 0x599, 0x07d, + 0x59a, 0x018, + 0x59c, 0x066, + 0x59d, 0x090, + 0x59e, 0x001, + 0x584, 0x000, + 0x585, 0x000, + 0x586, 0x003, + 0x588, 0x0ff, + 0x589, 0x00f, + 0x58a, 0x000, + 0x58b, 0x000, + 0x58c, 0x010, + 0x58d, 0x032, + 0x58e, 0x054, + 0x58f, 0x023, + 0x590, 0x000, + 0x595, 0x000, + 0x596, 0x000, + 0x597, 0x000, + 0x464, 0x000, + 0x46c, 0xbbbb10, + 0x470, 0x101010, + + 0x478, 0x000, + 0x474, 0x018, + 0x454, 0x042135, + 0x598, 0x0e7, + 0x599, 0x07d, + 0x59a, 0x018, + 0x59c, 0x066, + 0x59d, 0x090, + 0x59e, 0x001, + 0x584, 0x000, + 0x585, 0x000, + 0x586, 0x003, + 0x588, 0x0ff, + 0x589, 0x00f, + 0x58a, 0x000, + 0x58b, 0x000, + 0x58c, 0x010, + 0x58d, 0x032, + 0x58e, 0x054, + 0x58f, 0x023, + 0x590, 0x000, + 0x595, 0x000, + 0x596, 0x000, + 0x597, 0x000, + 0x464, 0x000, + 0x46c, 0xbbbb10, + 0x470, 0x101010, + 0x478, 0x000, + 0x474, 0x018, + 0x454, 0x042135, + 0x193, 0x000, + 0x300, 0x000, + 0x301, 0x006, + 0x302, 0x000, + 0x303, 0x006, + 0x308, 0x040, + 0x309, 0x000, + 0x30a, 0x000, + 0x30b, 0x000, + 0x000, 0x002, + 0x001, 0x000, + 0x002, 0x000, + 0x003, 0x000, + 0x004, 0x033, + 0x040, 0x01d, + 0x041, 0x001, + 0x042, 0x004, + 0x043, 0x000, + 0x080, 0x01e, + 0x081, 0x001, + 0x082, 0x004, + 0x083, 0x000, + 0x190, 0x018, + 0x115, 0x000, + 0x116, 0x012, + 0x117, 0x018, + 0x04a, 0x011, + 0x08a, 0x011, + 0x04b, 0x000, + 0x08b, 0x000, + 0x048, 0x000, + 0x088, 0x000, + 0x04e, 0x012, + 0x08e, 0x012, + 0x058, 0x012, + 0x098, 0x012, + 0x059, 0x000, + 0x099, 0x000, + 0x05a, 0x003, + 0x09a, 0x003, + 0x05b, 0x001, + 0x09b, 0x001, + 0x054, 0x008, + 0x094, 0x008, + 0x055, 0x000, + 0x095, 0x000, + 0x056, 0x0c7, + 0x096, 0x0c7, + 0x057, 0x002, + 0x097, 0x002, + 0x060, 0x001, + 0x0a0, 0x001, + 0x061, 0x000, + 0x0a1, 0x000, + 0x062, 0x000, + 0x0a2, 0x000, + 0x063, 0x000, + 0x0a3, 0x000, + 0x070, 0x000, + 0x0b0, 0x000, + 0x071, 0x004, + 0x0b1, 0x004, + 0x06c, 0x0e9, + 0x0ac, 0x0e9, + 0x06d, 0x003, + 0x0ad, 0x003, + 0x05c, 0x0d0, + 0x09c, 0x0d0, + 0x05d, 0x002, + 0x09d, 0x002, + 0x05e, 0x0f2, + 0x09e, 0x0f2, + 0x05f, 0x000, + 0x09f, 0x000, + 0x074, 0x000, + 0x0b4, 0x000, + 0x075, 0x000, + 0x0b5, 0x000, + 0x076, 0x000, + 0x0b6, 0x000, + 0x077, 0x000, + 0x0b7, 0x000, + 0x195, 0x008, + 0x598, 0x0e7, + 0x599, 0x07d, + 0x59a, 0x018, + 0x59c, 0x066, + 0x59d, 0x090, + 0x59e, 0x001, + 0x584, 0x000, + 0x585, 0x000, + 0x586, 0x003, + 0x588, 0x0ff, + 0x589, 0x00f, + 0x58a, 0x000, + 0x58b, 0x000, + 0x58c, 0x010, + 0x58d, 0x032, + 0x58e, 0x054, + 0x58f, 0x023, + 0x590, 0x000, + 0x595, 0x000, + 0x596, 0x000, + 0x597, 0x000, + 0x464, 0x000, + 0x46c, 0xbbbb10, + 0x470, 0x101010, + 0x478, 0x000, + 0x474, 0x018, + 0x454, 0x042135, + 0x193, 0x0a6, + 0x108, 0x0f8, + 0x042, 0x003, + 0x082, 0x003, + 0x454, 0x0425b9, + 0x454, 0x042539, + 0x193, 0x000, + 0x193, 0x0a6, + 0x464, 0x000, + + 0, 0 +}; + +/* Tuner */ +static u32 reg_init_tuner_input[] = { + 0x108, 0x0f8, /* Sync control */ + 0x111, 0x000, /* Mode/delay control */ + 0x10e, 0x00a, /* Chroma control 1 */ + 0, 0 +}; + +/* Composite */ +static u32 reg_init_composite_input[] = { + 0x108, 0x0e8, /* Sync control */ + 0x111, 0x000, /* Mode/delay control */ + 0x10e, 0x04a, /* Chroma control 1 */ + 0, 0 +}; + +/* S-Video */ +static u32 reg_init_svideo_input[] = { + 0x108, 0x0e8, /* Sync control */ + 0x111, 0x000, /* Mode/delay control */ + 0x10e, 0x04a, /* Chroma control 1 */ + 0, 0 +}; + +static u32 reg_set_audio_template[4][2] = +{ + { /* for MONO + tadachi 6/29 DMA audio output select? + Register 0x46c + 7-4: DMA2, 3-0: DMA1 ch. DMA4, DMA3 DMA2, DMA1 + 0: MAIN left, 1: MAIN right + 2: AUX1 left, 3: AUX1 right + 4: AUX2 left, 5: AUX2 right + 6: DPL left, 7: DPL right + 8: DPL center, 9: DPL surround + A: monitor output, B: digital sense */ + 0xbbbb00, + + /* tadachi 6/29 DAC and I2S output select? + Register 0x470 + 7-4:DAC right ch. 3-0:DAC left ch. + I2S1 right,left I2S2 right,left */ + 0x00, + }, + { /* for STEREO */ + 0xbbbb10, 0x101010, + }, + { /* for LANG1 */ + 0xbbbb00, 0x00, + }, + { /* for LANG2/SAP */ + 0xbbbb11, 0x111111, + } +}; + + +/* Get detected audio flags (from saa7134 driver) */ +static void get_inf_dev_status(struct v4l2_subdev *sd, + int *dual_flag, int *stereo_flag) +{ + u32 reg_data3; + + 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]", + }; + + + *dual_flag = *stereo_flag = 0; + + /* (demdec status: 0x528) */ + + /* read current status */ + reg_data3 = saa717x_read(sd, 0x0528); + + v4l2_dbg(1, debug, sd, "tvaudio thread status: 0x%x [%s%s%s]\n", + reg_data3, stdres[reg_data3 & 0x1f], + (reg_data3 & 0x000020) ? ",stereo" : "", + (reg_data3 & 0x000040) ? ",dual" : ""); + v4l2_dbg(1, debug, sd, "detailed status: " + "%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s\n", + (reg_data3 & 0x000080) ? " A2/EIAJ pilot tone " : "", + (reg_data3 & 0x000100) ? " A2/EIAJ dual " : "", + (reg_data3 & 0x000200) ? " A2/EIAJ stereo " : "", + (reg_data3 & 0x000400) ? " A2/EIAJ noise mute " : "", + + (reg_data3 & 0x000800) ? " BTSC/FM radio pilot " : "", + (reg_data3 & 0x001000) ? " SAP carrier " : "", + (reg_data3 & 0x002000) ? " BTSC stereo noise mute " : "", + (reg_data3 & 0x004000) ? " SAP noise mute " : "", + (reg_data3 & 0x008000) ? " VDSP " : "", + + (reg_data3 & 0x010000) ? " NICST " : "", + (reg_data3 & 0x020000) ? " NICDU " : "", + (reg_data3 & 0x040000) ? " NICAM muted " : "", + (reg_data3 & 0x080000) ? " NICAM reserve sound " : "", + + (reg_data3 & 0x100000) ? " init done " : ""); + + if (reg_data3 & 0x000220) { + v4l2_dbg(1, debug, sd, "ST!!!\n"); + *stereo_flag = 1; + } + + if (reg_data3 & 0x000140) { + v4l2_dbg(1, debug, sd, "DUAL!!!\n"); + *dual_flag = 1; + } +} + +/* regs write to set audio mode */ +static void set_audio_mode(struct v4l2_subdev *sd, int audio_mode) +{ + v4l2_dbg(1, debug, sd, "writing registers to set audio mode by set %d\n", + audio_mode); + + saa717x_write(sd, 0x46c, reg_set_audio_template[audio_mode][0]); + saa717x_write(sd, 0x470, reg_set_audio_template[audio_mode][1]); +} + +/* write regs to set audio volume, bass and treble */ +static int set_audio_regs(struct v4l2_subdev *sd, + struct saa717x_state *decoder) +{ + u8 mute = 0xac; /* -84 dB */ + u32 val; + unsigned int work_l, work_r; + + /* set SIF analog I/O select */ + saa717x_write(sd, 0x0594, decoder->audio_input); + v4l2_dbg(1, debug, sd, "set audio input %d\n", + decoder->audio_input); + + /* normalize ( 65535 to 0 -> 24 to -40 (not -84)) */ + work_l = (min(65536 - decoder->audio_main_balance, 32768) * decoder->audio_main_volume) / 32768; + work_r = (min(decoder->audio_main_balance, (u16)32768) * decoder->audio_main_volume) / 32768; + decoder->audio_main_vol_l = (long)work_l * (24 - (-40)) / 65535 - 40; + decoder->audio_main_vol_r = (long)work_r * (24 - (-40)) / 65535 - 40; + + /* set main volume */ + /* main volume L[7-0],R[7-0],0x00 24=24dB,-83dB, -84(mute) */ + /* def:0dB->6dB(MPG600GR) */ + /* if mute is on, set mute */ + if (decoder->audio_main_mute) { + val = mute | (mute << 8); + } else { + val = (u8)decoder->audio_main_vol_l | + ((u8)decoder->audio_main_vol_r << 8); + } + + saa717x_write(sd, 0x480, val); + + /* set bass and treble */ + val = decoder->audio_main_bass & 0x1f; + val |= (decoder->audio_main_treble & 0x1f) << 5; + saa717x_write(sd, 0x488, val); + return 0; +} + +/********** scaling staff ***********/ +static void set_h_prescale(struct v4l2_subdev *sd, + 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, task_shift; + + task_shift = task * 0x40; + for (i = 0; i < count; i++) + if (vals[i].xpsc == prescale) + break; + if (i == count) + return; + + /* horizonal prescaling */ + saa717x_write(sd, 0x60 + task_shift, vals[i].xpsc); + /* accumulation length */ + saa717x_write(sd, 0x61 + task_shift, vals[i].xacl); + /* level control */ + saa717x_write(sd, 0x62 + task_shift, + (vals[i].xc2_1 << 3) | vals[i].xdcg); + /*FIR prefilter control */ + saa717x_write(sd, 0x63 + task_shift, + (vals[i].vpfy << 2) | vals[i].vpfy); +} + +/********** scaling staff ***********/ +static void set_v_scale(struct v4l2_subdev *sd, int task, int yscale) +{ + int task_shift; + + task_shift = task * 0x40; + /* Vertical scaling ratio (LOW) */ + saa717x_write(sd, 0x70 + task_shift, yscale & 0xff); + /* Vertical scaling ratio (HI) */ + saa717x_write(sd, 0x71 + task_shift, yscale >> 8); +} + +static int saa717x_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct saa717x_state *state = to_state(sd); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + saa717x_write(sd, 0x10a, ctrl->val); + return 0; + + case V4L2_CID_CONTRAST: + saa717x_write(sd, 0x10b, ctrl->val); + return 0; + + case V4L2_CID_SATURATION: + saa717x_write(sd, 0x10c, ctrl->val); + return 0; + + case V4L2_CID_HUE: + saa717x_write(sd, 0x10d, ctrl->val); + return 0; + + case V4L2_CID_AUDIO_MUTE: + state->audio_main_mute = ctrl->val; + break; + + case V4L2_CID_AUDIO_VOLUME: + state->audio_main_volume = ctrl->val; + break; + + case V4L2_CID_AUDIO_BALANCE: + state->audio_main_balance = ctrl->val; + break; + + case V4L2_CID_AUDIO_TREBLE: + state->audio_main_treble = ctrl->val; + break; + + case V4L2_CID_AUDIO_BASS: + state->audio_main_bass = ctrl->val; + break; + + default: + return 0; + } + set_audio_regs(sd, state); + return 0; +} + +static int saa717x_s_video_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct saa717x_state *decoder = to_state(sd); + int is_tuner = input & 0x80; /* tuner input flag */ + + input &= 0x7f; + + v4l2_dbg(1, debug, sd, "decoder set input (%d)\n", input); + /* inputs from 0-9 are available*/ + /* saa717x have mode0-mode9 but mode5 is reserved. */ + if (input > 9 || input == 5) + return -EINVAL; + + if (decoder->input != input) { + int input_line = input; + + decoder->input = input_line; + v4l2_dbg(1, debug, sd, "now setting %s input %d\n", + input_line >= 6 ? "S-Video" : "Composite", + input_line); + + /* select mode */ + saa717x_write(sd, 0x102, + (saa717x_read(sd, 0x102) & 0xf0) | + input_line); + + /* bypass chrominance trap for modes 6..9 */ + saa717x_write(sd, 0x109, + (saa717x_read(sd, 0x109) & 0x7f) | + (input_line < 6 ? 0x0 : 0x80)); + + /* change audio_mode */ + if (is_tuner) { + /* tuner */ + set_audio_mode(sd, decoder->tuner_audio_mode); + } else { + /* Force to STEREO mode if Composite or + * S-Video were chosen */ + set_audio_mode(sd, TUNER_AUDIO_STEREO); + } + /* change initialize procedure (Composite/S-Video) */ + if (is_tuner) + saa717x_write_regs(sd, reg_init_tuner_input); + else if (input_line >= 6) + saa717x_write_regs(sd, reg_init_svideo_input); + else + saa717x_write_regs(sd, reg_init_composite_input); + } + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int saa717x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = saa717x_read(sd, reg->reg); + reg->size = 1; + return 0; +} + +static int saa717x_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u16 addr = reg->reg & 0xffff; + u8 val = reg->val & 0xff; + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + saa717x_write(sd, addr, val); + return 0; +} +#endif + +static int saa717x_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) +{ + int prescale, h_scale, v_scale; + + v4l2_dbg(1, debug, sd, "decoder set size\n"); + + if (fmt->code != V4L2_MBUS_FMT_FIXED) + return -EINVAL; + + /* FIXME need better bounds checking here */ + if (fmt->width < 1 || fmt->width > 1440) + return -EINVAL; + if (fmt->height < 1 || fmt->height > 960) + return -EINVAL; + + fmt->field = V4L2_FIELD_INTERLACED; + fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + + /* scaling setting */ + /* NTSC and interlace only */ + prescale = SAA717X_NTSC_WIDTH / fmt->width; + if (prescale == 0) + prescale = 1; + h_scale = 1024 * SAA717X_NTSC_WIDTH / prescale / fmt->width; + /* interlace */ + v_scale = 512 * 2 * SAA717X_NTSC_HEIGHT / fmt->height; + + /* Horizontal prescaling etc */ + set_h_prescale(sd, 0, prescale); + set_h_prescale(sd, 1, prescale); + + /* Horizontal scaling increment */ + /* TASK A */ + saa717x_write(sd, 0x6C, (u8)(h_scale & 0xFF)); + saa717x_write(sd, 0x6D, (u8)((h_scale >> 8) & 0xFF)); + /* TASK B */ + saa717x_write(sd, 0xAC, (u8)(h_scale & 0xFF)); + saa717x_write(sd, 0xAD, (u8)((h_scale >> 8) & 0xFF)); + + /* Vertical prescaling etc */ + set_v_scale(sd, 0, v_scale); + set_v_scale(sd, 1, v_scale); + + /* set video output size */ + /* video number of pixels at output */ + /* TASK A */ + saa717x_write(sd, 0x5C, (u8)(fmt->width & 0xFF)); + saa717x_write(sd, 0x5D, (u8)((fmt->width >> 8) & 0xFF)); + /* TASK B */ + saa717x_write(sd, 0x9C, (u8)(fmt->width & 0xFF)); + saa717x_write(sd, 0x9D, (u8)((fmt->width >> 8) & 0xFF)); + + /* video number of lines at output */ + /* TASK A */ + saa717x_write(sd, 0x5E, (u8)(fmt->height & 0xFF)); + saa717x_write(sd, 0x5F, (u8)((fmt->height >> 8) & 0xFF)); + /* TASK B */ + saa717x_write(sd, 0x9E, (u8)(fmt->height & 0xFF)); + saa717x_write(sd, 0x9F, (u8)((fmt->height >> 8) & 0xFF)); + return 0; +} + +static int saa717x_s_radio(struct v4l2_subdev *sd) +{ + struct saa717x_state *decoder = to_state(sd); + + decoder->radio = 1; + return 0; +} + +static int saa717x_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct saa717x_state *decoder = to_state(sd); + + v4l2_dbg(1, debug, sd, "decoder set norm "); + v4l2_dbg(1, debug, sd, "(not yet implementd)\n"); + + decoder->radio = 0; + decoder->std = std; + return 0; +} + +static int saa717x_s_audio_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct saa717x_state *decoder = to_state(sd); + + if (input < 3) { /* FIXME! --tadachi */ + decoder->audio_input = input; + v4l2_dbg(1, debug, sd, + "set decoder audio input to %d\n", + decoder->audio_input); + set_audio_regs(sd, decoder); + return 0; + } + return -ERANGE; +} + +static int saa717x_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct saa717x_state *decoder = to_state(sd); + + v4l2_dbg(1, debug, sd, "decoder %s output\n", + enable ? "enable" : "disable"); + decoder->enable = enable; + saa717x_write(sd, 0x193, enable ? 0xa6 : 0x26); + return 0; +} + +/* change audio mode */ +static int saa717x_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct saa717x_state *decoder = to_state(sd); + int audio_mode; + char *mes[4] = { + "MONO", "STEREO", "LANG1", "LANG2/SAP" + }; + + audio_mode = TUNER_AUDIO_STEREO; + + switch (vt->audmode) { + case V4L2_TUNER_MODE_MONO: + audio_mode = TUNER_AUDIO_MONO; + break; + case V4L2_TUNER_MODE_STEREO: + audio_mode = TUNER_AUDIO_STEREO; + break; + case V4L2_TUNER_MODE_LANG2: + audio_mode = TUNER_AUDIO_LANG2; + break; + case V4L2_TUNER_MODE_LANG1: + audio_mode = TUNER_AUDIO_LANG1; + break; + } + + v4l2_dbg(1, debug, sd, "change audio mode to %s\n", + mes[audio_mode]); + decoder->tuner_audio_mode = audio_mode; + /* The registers are not changed here. */ + /* See DECODER_ENABLE_OUTPUT section. */ + set_audio_mode(sd, decoder->tuner_audio_mode); + return 0; +} + +static int saa717x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct saa717x_state *decoder = to_state(sd); + int dual_f, stereo_f; + + if (decoder->radio) + return 0; + get_inf_dev_status(sd, &dual_f, &stereo_f); + + v4l2_dbg(1, debug, sd, "DETECT==st:%d dual:%d\n", + stereo_f, dual_f); + + /* mono */ + if ((dual_f == 0) && (stereo_f == 0)) { + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + v4l2_dbg(1, debug, sd, "DETECT==MONO\n"); + } + + /* stereo */ + if (stereo_f == 1) { + if (vt->audmode == V4L2_TUNER_MODE_STEREO || + vt->audmode == V4L2_TUNER_MODE_LANG1) { + vt->rxsubchans = V4L2_TUNER_SUB_STEREO; + v4l2_dbg(1, debug, sd, "DETECT==ST(ST)\n"); + } else { + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + v4l2_dbg(1, debug, sd, "DETECT==ST(MONO)\n"); + } + } + + /* dual */ + if (dual_f == 1) { + if (vt->audmode == V4L2_TUNER_MODE_LANG2) { + vt->rxsubchans = V4L2_TUNER_SUB_LANG2 | V4L2_TUNER_SUB_MONO; + v4l2_dbg(1, debug, sd, "DETECT==DUAL1\n"); + } else { + vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_MONO; + v4l2_dbg(1, debug, sd, "DETECT==DUAL2\n"); + } + } + return 0; +} + +static int saa717x_log_status(struct v4l2_subdev *sd) +{ + struct saa717x_state *state = to_state(sd); + + v4l2_ctrl_handler_log_status(&state->hdl, sd->name); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops saa717x_ctrl_ops = { + .s_ctrl = saa717x_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops saa717x_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = saa717x_g_register, + .s_register = saa717x_s_register, +#endif + .s_std = saa717x_s_std, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, + .log_status = saa717x_log_status, +}; + +static const struct v4l2_subdev_tuner_ops saa717x_tuner_ops = { + .g_tuner = saa717x_g_tuner, + .s_tuner = saa717x_s_tuner, + .s_radio = saa717x_s_radio, +}; + +static const struct v4l2_subdev_video_ops saa717x_video_ops = { + .s_routing = saa717x_s_video_routing, + .s_mbus_fmt = saa717x_s_mbus_fmt, + .s_stream = saa717x_s_stream, +}; + +static const struct v4l2_subdev_audio_ops saa717x_audio_ops = { + .s_routing = saa717x_s_audio_routing, +}; + +static const struct v4l2_subdev_ops saa717x_ops = { + .core = &saa717x_core_ops, + .tuner = &saa717x_tuner_ops, + .audio = &saa717x_audio_ops, + .video = &saa717x_video_ops, +}; + +/* ----------------------------------------------------------------------- */ + + +/* i2c implementation */ + +/* ----------------------------------------------------------------------- */ +static int saa717x_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct saa717x_state *decoder; + struct v4l2_ctrl_handler *hdl; + struct v4l2_subdev *sd; + u8 id = 0; + char *p = ""; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + decoder = kzalloc(sizeof(struct saa717x_state), GFP_KERNEL); + if (decoder == NULL) + return -ENOMEM; + + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &saa717x_ops); + + if (saa717x_write(sd, 0x5a4, 0xfe) && + saa717x_write(sd, 0x5a5, 0x0f) && + saa717x_write(sd, 0x5a6, 0x00) && + saa717x_write(sd, 0x5a7, 0x01)) + id = saa717x_read(sd, 0x5a0); + if (id != 0xc2 && id != 0x32 && id != 0xf2 && id != 0x6c) { + v4l2_dbg(1, debug, sd, "saa717x not found (id=%02x)\n", id); + kfree(decoder); + return -ENODEV; + } + if (id == 0xc2) + p = "saa7173"; + else if (id == 0x32) + p = "saa7174A"; + else if (id == 0x6c) + p = "saa7174HL"; + else + p = "saa7171"; + v4l2_info(sd, "%s found @ 0x%x (%s)\n", p, + client->addr << 1, client->adapter->name); + + hdl = &decoder->hdl; + v4l2_ctrl_handler_init(hdl, 9); + /* add in ascending ID order */ + v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 68); + v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 64); + v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 42000); + v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, + V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768); + v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, + V4L2_CID_AUDIO_BASS, -16, 15, 1, 0); + v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, + V4L2_CID_AUDIO_TREBLE, -16, 15, 1, 0); + v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + sd->ctrl_handler = hdl; + if (hdl->error) { + int err = hdl->error; + + v4l2_ctrl_handler_free(hdl); + kfree(decoder); + return err; + } + + decoder->std = V4L2_STD_NTSC; + decoder->input = -1; + decoder->enable = 1; + + /* FIXME!! */ + decoder->playback = 0; /* initially capture mode used */ + decoder->audio = 1; /* DECODER_AUDIO_48_KHZ */ + + decoder->audio_input = 2; /* FIXME!! */ + + decoder->tuner_audio_mode = TUNER_AUDIO_STEREO; + /* set volume, bass and treble */ + decoder->audio_main_vol_l = 6; + decoder->audio_main_vol_r = 6; + + v4l2_dbg(1, debug, sd, "writing init values\n"); + + /* FIXME!! */ + saa717x_write_regs(sd, reg_init_initialize); + + v4l2_ctrl_handler_setup(hdl); + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(2*HZ); + return 0; +} + +static int saa717x_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); + kfree(to_state(sd)); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id saa717x_id[] = { + { "saa717x", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa717x_id); + +static struct i2c_driver saa717x_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "saa717x", + }, + .probe = saa717x_probe, + .remove = saa717x_remove, + .id_table = saa717x_id, +}; + +module_i2c_driver(saa717x_driver); diff --git a/drivers/media/i2c/saa7185.c b/drivers/media/i2c/saa7185.c new file mode 100644 index 00000000000..2c6b65c76e2 --- /dev/null +++ b/drivers/media/i2c/saa7185.c @@ -0,0 +1,377 @@ +/* + * saa7185 - Philips SAA7185B video encoder driver version 0.0.3 + * + * Copyright (C) 1998 Dave Perks + * + * Slight changes for video timing and attachment output by + * Wolfgang Scherr + * + * Changes by Ronald Bultje + * - moved over to linux>=2.4.x i2c protocol (1/1/2003) + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("Philips SAA7185 video encoder driver"); +MODULE_AUTHOR("Dave Perks"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + + +/* ----------------------------------------------------------------------- */ + +struct saa7185 { + struct v4l2_subdev sd; + unsigned char reg[128]; + + v4l2_std_id norm; +}; + +static inline struct saa7185 *to_saa7185(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa7185, sd); +} + +/* ----------------------------------------------------------------------- */ + +static inline int saa7185_read(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_read_byte(client); +} + +static int saa7185_write(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct saa7185 *encoder = to_saa7185(sd); + + v4l2_dbg(1, debug, sd, "%02x set to %02x\n", reg, value); + encoder->reg[reg] = value; + return i2c_smbus_write_byte_data(client, reg, value); +} + +static int saa7185_write_block(struct v4l2_subdev *sd, + const u8 *data, unsigned int len) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct saa7185 *encoder = to_saa7185(sd); + int ret = -1; + u8 reg; + + /* the adv7175 has an autoincrement function, use it if + * the adapter understands raw I2C */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + /* do raw I2C, not smbus compatible */ + u8 block_data[32]; + int block_len; + + while (len >= 2) { + block_len = 0; + block_data[block_len++] = reg = data[0]; + do { + block_data[block_len++] = + encoder->reg[reg++] = data[1]; + len -= 2; + data += 2; + } while (len >= 2 && data[0] == reg && block_len < 32); + ret = i2c_master_send(client, block_data, block_len); + if (ret < 0) + break; + } + } else { + /* do some slow I2C emulation kind of thing */ + while (len >= 2) { + reg = *data++; + ret = saa7185_write(sd, reg, *data++); + if (ret < 0) + break; + len -= 2; + } + } + + return ret; +} + +/* ----------------------------------------------------------------------- */ + +static const unsigned char init_common[] = { + 0x3a, 0x0f, /* CBENB=0, V656=0, VY2C=1, + * YUV2C=1, MY2C=1, MUV2C=1 */ + + 0x42, 0x6b, /* OVLY0=107 */ + 0x43, 0x00, /* OVLU0=0 white */ + 0x44, 0x00, /* OVLV0=0 */ + 0x45, 0x22, /* OVLY1=34 */ + 0x46, 0xac, /* OVLU1=172 yellow */ + 0x47, 0x0e, /* OVLV1=14 */ + 0x48, 0x03, /* OVLY2=3 */ + 0x49, 0x1d, /* OVLU2=29 cyan */ + 0x4a, 0xac, /* OVLV2=172 */ + 0x4b, 0xf0, /* OVLY3=240 */ + 0x4c, 0xc8, /* OVLU3=200 green */ + 0x4d, 0xb9, /* OVLV3=185 */ + 0x4e, 0xd4, /* OVLY4=212 */ + 0x4f, 0x38, /* OVLU4=56 magenta */ + 0x50, 0x47, /* OVLV4=71 */ + 0x51, 0xc1, /* OVLY5=193 */ + 0x52, 0xe3, /* OVLU5=227 red */ + 0x53, 0x54, /* OVLV5=84 */ + 0x54, 0xa3, /* OVLY6=163 */ + 0x55, 0x54, /* OVLU6=84 blue */ + 0x56, 0xf2, /* OVLV6=242 */ + 0x57, 0x90, /* OVLY7=144 */ + 0x58, 0x00, /* OVLU7=0 black */ + 0x59, 0x00, /* OVLV7=0 */ + + 0x5a, 0x00, /* CHPS=0 */ + 0x5b, 0x76, /* GAINU=118 */ + 0x5c, 0xa5, /* GAINV=165 */ + 0x5d, 0x3c, /* BLCKL=60 */ + 0x5e, 0x3a, /* BLNNL=58 */ + 0x5f, 0x3a, /* CCRS=0, BLNVB=58 */ + 0x60, 0x00, /* NULL */ + + /* 0x61 - 0x66 set according to norm */ + + 0x67, 0x00, /* 0 : caption 1st byte odd field */ + 0x68, 0x00, /* 0 : caption 2nd byte odd field */ + 0x69, 0x00, /* 0 : caption 1st byte even field */ + 0x6a, 0x00, /* 0 : caption 2nd byte even field */ + + 0x6b, 0x91, /* MODIN=2, PCREF=0, SCCLN=17 */ + 0x6c, 0x20, /* SRCV1=0, TRCV2=1, ORCV1=0, PRCV1=0, + * CBLF=0, ORCV2=0, PRCV2=0 */ + 0x6d, 0x00, /* SRCM1=0, CCEN=0 */ + + 0x6e, 0x0e, /* HTRIG=0x005, approx. centered, at + * least for PAL */ + 0x6f, 0x00, /* HTRIG upper bits */ + 0x70, 0x20, /* PHRES=0, SBLN=1, VTRIG=0 */ + + /* The following should not be needed */ + + 0x71, 0x15, /* BMRQ=0x115 */ + 0x72, 0x90, /* EMRQ=0x690 */ + 0x73, 0x61, /* EMRQ=0x690, BMRQ=0x115 */ + 0x74, 0x00, /* NULL */ + 0x75, 0x00, /* NULL */ + 0x76, 0x00, /* NULL */ + 0x77, 0x15, /* BRCV=0x115 */ + 0x78, 0x90, /* ERCV=0x690 */ + 0x79, 0x61, /* ERCV=0x690, BRCV=0x115 */ + + /* Field length controls */ + + 0x7a, 0x70, /* FLC=0 */ + + /* The following should not be needed if SBLN = 1 */ + + 0x7b, 0x16, /* FAL=22 */ + 0x7c, 0x35, /* LAL=244 */ + 0x7d, 0x20, /* LAL=244, FAL=22 */ +}; + +static const unsigned char init_pal[] = { + 0x61, 0x1e, /* FISE=0, PAL=1, SCBW=1, RTCE=1, + * YGS=1, INPI=0, DOWN=0 */ + 0x62, 0xc8, /* DECTYP=1, BSTA=72 */ + 0x63, 0xcb, /* FSC0 */ + 0x64, 0x8a, /* FSC1 */ + 0x65, 0x09, /* FSC2 */ + 0x66, 0x2a, /* FSC3 */ +}; + +static const unsigned char init_ntsc[] = { + 0x61, 0x1d, /* FISE=1, PAL=0, SCBW=1, RTCE=1, + * YGS=1, INPI=0, DOWN=0 */ + 0x62, 0xe6, /* DECTYP=1, BSTA=102 */ + 0x63, 0x1f, /* FSC0 */ + 0x64, 0x7c, /* FSC1 */ + 0x65, 0xf0, /* FSC2 */ + 0x66, 0x21, /* FSC3 */ +}; + + +static int saa7185_init(struct v4l2_subdev *sd, u32 val) +{ + struct saa7185 *encoder = to_saa7185(sd); + + saa7185_write_block(sd, init_common, sizeof(init_common)); + if (encoder->norm & V4L2_STD_NTSC) + saa7185_write_block(sd, init_ntsc, sizeof(init_ntsc)); + else + saa7185_write_block(sd, init_pal, sizeof(init_pal)); + return 0; +} + +static int saa7185_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct saa7185 *encoder = to_saa7185(sd); + + if (std & V4L2_STD_NTSC) + saa7185_write_block(sd, init_ntsc, sizeof(init_ntsc)); + else if (std & V4L2_STD_PAL) + saa7185_write_block(sd, init_pal, sizeof(init_pal)); + else + return -EINVAL; + encoder->norm = std; + return 0; +} + +static int saa7185_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct saa7185 *encoder = to_saa7185(sd); + + /* RJ: input = 0: input is from SA7111 + input = 1: input is from ZR36060 */ + + switch (input) { + case 0: + /* turn off colorbar */ + saa7185_write(sd, 0x3a, 0x0f); + /* Switch RTCE to 1 */ + saa7185_write(sd, 0x61, (encoder->reg[0x61] & 0xf7) | 0x08); + saa7185_write(sd, 0x6e, 0x01); + break; + + case 1: + /* turn off colorbar */ + saa7185_write(sd, 0x3a, 0x0f); + /* Switch RTCE to 0 */ + saa7185_write(sd, 0x61, (encoder->reg[0x61] & 0xf7) | 0x00); + /* SW: a slight sync problem... */ + saa7185_write(sd, 0x6e, 0x00); + break; + + case 2: + /* turn on colorbar */ + saa7185_write(sd, 0x3a, 0x8f); + /* Switch RTCE to 0 */ + saa7185_write(sd, 0x61, (encoder->reg[0x61] & 0xf7) | 0x08); + /* SW: a slight sync problem... */ + saa7185_write(sd, 0x6e, 0x01); + break; + + default: + return -EINVAL; + } + return 0; +} + +static int saa7185_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7185, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops saa7185_core_ops = { + .g_chip_ident = saa7185_g_chip_ident, + .init = saa7185_init, +}; + +static const struct v4l2_subdev_video_ops saa7185_video_ops = { + .s_std_output = saa7185_s_std_output, + .s_routing = saa7185_s_routing, +}; + +static const struct v4l2_subdev_ops saa7185_ops = { + .core = &saa7185_core_ops, + .video = &saa7185_video_ops, +}; + + +/* ----------------------------------------------------------------------- */ + +static int saa7185_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int i; + struct saa7185 *encoder; + struct v4l2_subdev *sd; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + encoder = kzalloc(sizeof(struct saa7185), GFP_KERNEL); + if (encoder == NULL) + return -ENOMEM; + encoder->norm = V4L2_STD_NTSC; + sd = &encoder->sd; + v4l2_i2c_subdev_init(sd, client, &saa7185_ops); + + i = saa7185_write_block(sd, init_common, sizeof(init_common)); + if (i >= 0) + i = saa7185_write_block(sd, init_ntsc, sizeof(init_ntsc)); + if (i < 0) + v4l2_dbg(1, debug, sd, "init error %d\n", i); + else + v4l2_dbg(1, debug, sd, "revision 0x%x\n", + saa7185_read(sd) >> 5); + return 0; +} + +static int saa7185_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct saa7185 *encoder = to_saa7185(sd); + + v4l2_device_unregister_subdev(sd); + /* SW: output off is active */ + saa7185_write(sd, 0x61, (encoder->reg[0x61]) | 0x40); + kfree(encoder); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id saa7185_id[] = { + { "saa7185", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa7185_id); + +static struct i2c_driver saa7185_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "saa7185", + }, + .probe = saa7185_probe, + .remove = saa7185_remove, + .id_table = saa7185_id, +}; + +module_i2c_driver(saa7185_driver); diff --git a/drivers/media/i2c/saa7191.c b/drivers/media/i2c/saa7191.c new file mode 100644 index 00000000000..d7d1670e0ca --- /dev/null +++ b/drivers/media/i2c/saa7191.c @@ -0,0 +1,659 @@ +/* + * saa7191.c - Philips SAA7191 video decoder driver + * + * Copyright (C) 2003 Ladislav Michl + * Copyright (C) 2004,2005 Mikael Nousiainen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "saa7191.h" + +#define SAA7191_MODULE_VERSION "0.0.5" + +MODULE_DESCRIPTION("Philips SAA7191 video decoder driver"); +MODULE_VERSION(SAA7191_MODULE_VERSION); +MODULE_AUTHOR("Mikael Nousiainen "); +MODULE_LICENSE("GPL"); + + +// #define SAA7191_DEBUG + +#ifdef SAA7191_DEBUG +#define dprintk(x...) printk("SAA7191: " x); +#else +#define dprintk(x...) +#endif + +#define SAA7191_SYNC_COUNT 30 +#define SAA7191_SYNC_DELAY 100 /* milliseconds */ + +struct saa7191 { + struct v4l2_subdev sd; + + /* the register values are stored here as the actual + * I2C-registers are write-only */ + u8 reg[25]; + + int input; + v4l2_std_id norm; +}; + +static inline struct saa7191 *to_saa7191(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa7191, sd); +} + +static const u8 initseq[] = { + 0, /* Subaddress */ + + 0x50, /* (0x50) SAA7191_REG_IDEL */ + + /* 50 Hz signal timing */ + 0x30, /* (0x30) SAA7191_REG_HSYB */ + 0x00, /* (0x00) SAA7191_REG_HSYS */ + 0xe8, /* (0xe8) SAA7191_REG_HCLB */ + 0xb6, /* (0xb6) SAA7191_REG_HCLS */ + 0xf4, /* (0xf4) SAA7191_REG_HPHI */ + + /* control */ + SAA7191_LUMA_APER_1, /* (0x01) SAA7191_REG_LUMA - CVBS mode */ + 0x00, /* (0x00) SAA7191_REG_HUEC */ + 0xf8, /* (0xf8) SAA7191_REG_CKTQ */ + 0xf8, /* (0xf8) SAA7191_REG_CKTS */ + 0x90, /* (0x90) SAA7191_REG_PLSE */ + 0x90, /* (0x90) SAA7191_REG_SESE */ + 0x00, /* (0x00) SAA7191_REG_GAIN */ + SAA7191_STDC_NFEN | SAA7191_STDC_HRMV, /* (0x0c) SAA7191_REG_STDC + * - not SECAM, + * slow time constant */ + SAA7191_IOCK_OEDC | SAA7191_IOCK_OEHS | SAA7191_IOCK_OEVS + | SAA7191_IOCK_OEDY, /* (0x78) SAA7191_REG_IOCK + * - chroma from CVBS, GPSW1 & 2 off */ + SAA7191_CTL3_AUFD | SAA7191_CTL3_SCEN | SAA7191_CTL3_OFTS + | SAA7191_CTL3_YDEL0, /* (0x99) SAA7191_REG_CTL3 + * - automatic field detection */ + 0x00, /* (0x00) SAA7191_REG_CTL4 */ + 0x2c, /* (0x2c) SAA7191_REG_CHCV - PAL nominal value */ + 0x00, /* unused */ + 0x00, /* unused */ + + /* 60 Hz signal timing */ + 0x34, /* (0x34) SAA7191_REG_HS6B */ + 0x0a, /* (0x0a) SAA7191_REG_HS6S */ + 0xf4, /* (0xf4) SAA7191_REG_HC6B */ + 0xce, /* (0xce) SAA7191_REG_HC6S */ + 0xf4, /* (0xf4) SAA7191_REG_HP6I */ +}; + +/* SAA7191 register handling */ + +static u8 saa7191_read_reg(struct v4l2_subdev *sd, u8 reg) +{ + return to_saa7191(sd)->reg[reg]; +} + +static int saa7191_read_status(struct v4l2_subdev *sd, u8 *value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + + ret = i2c_master_recv(client, value, 1); + if (ret < 0) { + printk(KERN_ERR "SAA7191: saa7191_read_status(): read failed\n"); + return ret; + } + + return 0; +} + + +static int saa7191_write_reg(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + to_saa7191(sd)->reg[reg] = value; + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* the first byte of data must be the first subaddress number (register) */ +static int saa7191_write_block(struct v4l2_subdev *sd, + u8 length, const u8 *data) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct saa7191 *decoder = to_saa7191(sd); + int i; + int ret; + + for (i = 0; i < (length - 1); i++) { + decoder->reg[data[0] + i] = data[i + 1]; + } + + ret = i2c_master_send(client, data, length); + if (ret < 0) { + printk(KERN_ERR "SAA7191: saa7191_write_block(): " + "write failed\n"); + return ret; + } + + return 0; +} + +/* Helper functions */ + +static int saa7191_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct saa7191 *decoder = to_saa7191(sd); + u8 luma = saa7191_read_reg(sd, SAA7191_REG_LUMA); + u8 iock = saa7191_read_reg(sd, SAA7191_REG_IOCK); + int err; + + switch (input) { + case SAA7191_INPUT_COMPOSITE: /* Set Composite input */ + iock &= ~(SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW1 + | SAA7191_IOCK_GPSW2); + /* Chrominance trap active */ + luma &= ~SAA7191_LUMA_BYPS; + break; + case SAA7191_INPUT_SVIDEO: /* Set S-Video input */ + iock |= SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW2; + /* Chrominance trap bypassed */ + luma |= SAA7191_LUMA_BYPS; + break; + default: + return -EINVAL; + } + + err = saa7191_write_reg(sd, SAA7191_REG_LUMA, luma); + if (err) + return -EIO; + err = saa7191_write_reg(sd, SAA7191_REG_IOCK, iock); + if (err) + return -EIO; + + decoder->input = input; + + return 0; +} + +static int saa7191_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + struct saa7191 *decoder = to_saa7191(sd); + u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC); + u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3); + u8 chcv = saa7191_read_reg(sd, SAA7191_REG_CHCV); + int err; + + if (norm & V4L2_STD_PAL) { + stdc &= ~SAA7191_STDC_SECS; + ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL); + chcv = SAA7191_CHCV_PAL; + } else if (norm & V4L2_STD_NTSC) { + stdc &= ~SAA7191_STDC_SECS; + ctl3 &= ~SAA7191_CTL3_AUFD; + ctl3 |= SAA7191_CTL3_FSEL; + chcv = SAA7191_CHCV_NTSC; + } else if (norm & V4L2_STD_SECAM) { + stdc |= SAA7191_STDC_SECS; + ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL); + chcv = SAA7191_CHCV_PAL; + } else { + return -EINVAL; + } + + err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3); + if (err) + return -EIO; + err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc); + if (err) + return -EIO; + err = saa7191_write_reg(sd, SAA7191_REG_CHCV, chcv); + if (err) + return -EIO; + + decoder->norm = norm; + + dprintk("ctl3: %02x stdc: %02x chcv: %02x\n", ctl3, + stdc, chcv); + dprintk("norm: %llx\n", norm); + + return 0; +} + +static int saa7191_wait_for_signal(struct v4l2_subdev *sd, u8 *status) +{ + int i = 0; + + dprintk("Checking for signal...\n"); + + for (i = 0; i < SAA7191_SYNC_COUNT; i++) { + if (saa7191_read_status(sd, status)) + return -EIO; + + if (((*status) & SAA7191_STATUS_HLCK) == 0) { + dprintk("Signal found\n"); + return 0; + } + + msleep(SAA7191_SYNC_DELAY); + } + + dprintk("No signal\n"); + + return -EBUSY; +} + +static int saa7191_querystd(struct v4l2_subdev *sd, v4l2_std_id *norm) +{ + struct saa7191 *decoder = to_saa7191(sd); + u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC); + u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3); + u8 status; + v4l2_std_id old_norm = decoder->norm; + int err = 0; + + dprintk("SAA7191 extended signal auto-detection...\n"); + + *norm = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM; + stdc &= ~SAA7191_STDC_SECS; + ctl3 &= ~(SAA7191_CTL3_FSEL); + + err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc); + if (err) { + err = -EIO; + goto out; + } + err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3); + if (err) { + err = -EIO; + goto out; + } + + ctl3 |= SAA7191_CTL3_AUFD; + err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3); + if (err) { + err = -EIO; + goto out; + } + + msleep(SAA7191_SYNC_DELAY); + + err = saa7191_wait_for_signal(sd, &status); + if (err) + goto out; + + if (status & SAA7191_STATUS_FIDT) { + /* 60Hz signal -> NTSC */ + dprintk("60Hz signal: NTSC\n"); + *norm = V4L2_STD_NTSC; + return 0; + } + + /* 50Hz signal */ + dprintk("50Hz signal: Trying PAL...\n"); + + /* try PAL first */ + err = saa7191_s_std(sd, V4L2_STD_PAL); + if (err) + goto out; + + msleep(SAA7191_SYNC_DELAY); + + err = saa7191_wait_for_signal(sd, &status); + if (err) + goto out; + + /* not 50Hz ? */ + if (status & SAA7191_STATUS_FIDT) { + dprintk("No 50Hz signal\n"); + saa7191_s_std(sd, old_norm); + return -EAGAIN; + } + + if (status & SAA7191_STATUS_CODE) { + dprintk("PAL\n"); + *norm = V4L2_STD_PAL; + return saa7191_s_std(sd, old_norm); + } + + dprintk("No color detected with PAL - Trying SECAM...\n"); + + /* no color detected ? -> try SECAM */ + err = saa7191_s_std(sd, V4L2_STD_SECAM); + if (err) + goto out; + + msleep(SAA7191_SYNC_DELAY); + + err = saa7191_wait_for_signal(sd, &status); + if (err) + goto out; + + /* not 50Hz ? */ + if (status & SAA7191_STATUS_FIDT) { + dprintk("No 50Hz signal\n"); + err = -EAGAIN; + goto out; + } + + if (status & SAA7191_STATUS_CODE) { + /* Color detected -> SECAM */ + dprintk("SECAM\n"); + *norm = V4L2_STD_SECAM; + return saa7191_s_std(sd, old_norm); + } + + dprintk("No color detected with SECAM - Going back to PAL.\n"); + +out: + return saa7191_s_std(sd, old_norm); +} + +static int saa7191_autodetect_norm(struct v4l2_subdev *sd) +{ + u8 status; + + dprintk("SAA7191 signal auto-detection...\n"); + + dprintk("Reading status...\n"); + + if (saa7191_read_status(sd, &status)) + return -EIO; + + dprintk("Checking for signal...\n"); + + /* no signal ? */ + if (status & SAA7191_STATUS_HLCK) { + dprintk("No signal\n"); + return -EBUSY; + } + + dprintk("Signal found\n"); + + if (status & SAA7191_STATUS_FIDT) { + /* 60hz signal -> NTSC */ + dprintk("NTSC\n"); + return saa7191_s_std(sd, V4L2_STD_NTSC); + } else { + /* 50hz signal -> PAL */ + dprintk("PAL\n"); + return saa7191_s_std(sd, V4L2_STD_PAL); + } +} + +static int saa7191_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + u8 reg; + int ret = 0; + + switch (ctrl->id) { + case SAA7191_CONTROL_BANDPASS: + case SAA7191_CONTROL_BANDPASS_WEIGHT: + case SAA7191_CONTROL_CORING: + reg = saa7191_read_reg(sd, SAA7191_REG_LUMA); + switch (ctrl->id) { + case SAA7191_CONTROL_BANDPASS: + ctrl->value = ((s32)reg & SAA7191_LUMA_BPSS_MASK) + >> SAA7191_LUMA_BPSS_SHIFT; + break; + case SAA7191_CONTROL_BANDPASS_WEIGHT: + ctrl->value = ((s32)reg & SAA7191_LUMA_APER_MASK) + >> SAA7191_LUMA_APER_SHIFT; + break; + case SAA7191_CONTROL_CORING: + ctrl->value = ((s32)reg & SAA7191_LUMA_CORI_MASK) + >> SAA7191_LUMA_CORI_SHIFT; + break; + } + break; + case SAA7191_CONTROL_FORCE_COLOUR: + case SAA7191_CONTROL_CHROMA_GAIN: + reg = saa7191_read_reg(sd, SAA7191_REG_GAIN); + if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR) + ctrl->value = ((s32)reg & SAA7191_GAIN_COLO) ? 1 : 0; + else + ctrl->value = ((s32)reg & SAA7191_GAIN_LFIS_MASK) + >> SAA7191_GAIN_LFIS_SHIFT; + break; + case V4L2_CID_HUE: + reg = saa7191_read_reg(sd, SAA7191_REG_HUEC); + if (reg < 0x80) + reg += 0x80; + else + reg -= 0x80; + ctrl->value = (s32)reg; + break; + case SAA7191_CONTROL_VTRC: + reg = saa7191_read_reg(sd, SAA7191_REG_STDC); + ctrl->value = ((s32)reg & SAA7191_STDC_VTRC) ? 1 : 0; + break; + case SAA7191_CONTROL_LUMA_DELAY: + reg = saa7191_read_reg(sd, SAA7191_REG_CTL3); + ctrl->value = ((s32)reg & SAA7191_CTL3_YDEL_MASK) + >> SAA7191_CTL3_YDEL_SHIFT; + if (ctrl->value >= 4) + ctrl->value -= 8; + break; + case SAA7191_CONTROL_VNR: + reg = saa7191_read_reg(sd, SAA7191_REG_CTL4); + ctrl->value = ((s32)reg & SAA7191_CTL4_VNOI_MASK) + >> SAA7191_CTL4_VNOI_SHIFT; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int saa7191_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + u8 reg; + int ret = 0; + + switch (ctrl->id) { + case SAA7191_CONTROL_BANDPASS: + case SAA7191_CONTROL_BANDPASS_WEIGHT: + case SAA7191_CONTROL_CORING: + reg = saa7191_read_reg(sd, SAA7191_REG_LUMA); + switch (ctrl->id) { + case SAA7191_CONTROL_BANDPASS: + reg &= ~SAA7191_LUMA_BPSS_MASK; + reg |= (ctrl->value << SAA7191_LUMA_BPSS_SHIFT) + & SAA7191_LUMA_BPSS_MASK; + break; + case SAA7191_CONTROL_BANDPASS_WEIGHT: + reg &= ~SAA7191_LUMA_APER_MASK; + reg |= (ctrl->value << SAA7191_LUMA_APER_SHIFT) + & SAA7191_LUMA_APER_MASK; + break; + case SAA7191_CONTROL_CORING: + reg &= ~SAA7191_LUMA_CORI_MASK; + reg |= (ctrl->value << SAA7191_LUMA_CORI_SHIFT) + & SAA7191_LUMA_CORI_MASK; + break; + } + ret = saa7191_write_reg(sd, SAA7191_REG_LUMA, reg); + break; + case SAA7191_CONTROL_FORCE_COLOUR: + case SAA7191_CONTROL_CHROMA_GAIN: + reg = saa7191_read_reg(sd, SAA7191_REG_GAIN); + if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR) { + if (ctrl->value) + reg |= SAA7191_GAIN_COLO; + else + reg &= ~SAA7191_GAIN_COLO; + } else { + reg &= ~SAA7191_GAIN_LFIS_MASK; + reg |= (ctrl->value << SAA7191_GAIN_LFIS_SHIFT) + & SAA7191_GAIN_LFIS_MASK; + } + ret = saa7191_write_reg(sd, SAA7191_REG_GAIN, reg); + break; + case V4L2_CID_HUE: + reg = ctrl->value & 0xff; + if (reg < 0x80) + reg += 0x80; + else + reg -= 0x80; + ret = saa7191_write_reg(sd, SAA7191_REG_HUEC, reg); + break; + case SAA7191_CONTROL_VTRC: + reg = saa7191_read_reg(sd, SAA7191_REG_STDC); + if (ctrl->value) + reg |= SAA7191_STDC_VTRC; + else + reg &= ~SAA7191_STDC_VTRC; + ret = saa7191_write_reg(sd, SAA7191_REG_STDC, reg); + break; + case SAA7191_CONTROL_LUMA_DELAY: { + s32 value = ctrl->value; + if (value < 0) + value += 8; + reg = saa7191_read_reg(sd, SAA7191_REG_CTL3); + reg &= ~SAA7191_CTL3_YDEL_MASK; + reg |= (value << SAA7191_CTL3_YDEL_SHIFT) + & SAA7191_CTL3_YDEL_MASK; + ret = saa7191_write_reg(sd, SAA7191_REG_CTL3, reg); + break; + } + case SAA7191_CONTROL_VNR: + reg = saa7191_read_reg(sd, SAA7191_REG_CTL4); + reg &= ~SAA7191_CTL4_VNOI_MASK; + reg |= (ctrl->value << SAA7191_CTL4_VNOI_SHIFT) + & SAA7191_CTL4_VNOI_MASK; + ret = saa7191_write_reg(sd, SAA7191_REG_CTL4, reg); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +/* I2C-interface */ + +static int saa7191_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + u8 status_reg; + int res = V4L2_IN_ST_NO_SIGNAL; + + if (saa7191_read_status(sd, &status_reg)) + return -EIO; + if ((status_reg & SAA7191_STATUS_HLCK) == 0) + res = 0; + if (!(status_reg & SAA7191_STATUS_CODE)) + res |= V4L2_IN_ST_NO_COLOR; + *status = res; + return 0; +} + + +static int saa7191_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7191, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops saa7191_core_ops = { + .g_chip_ident = saa7191_g_chip_ident, + .g_ctrl = saa7191_g_ctrl, + .s_ctrl = saa7191_s_ctrl, + .s_std = saa7191_s_std, +}; + +static const struct v4l2_subdev_video_ops saa7191_video_ops = { + .s_routing = saa7191_s_routing, + .querystd = saa7191_querystd, + .g_input_status = saa7191_g_input_status, +}; + +static const struct v4l2_subdev_ops saa7191_ops = { + .core = &saa7191_core_ops, + .video = &saa7191_video_ops, +}; + +static int saa7191_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err = 0; + struct saa7191 *decoder; + struct v4l2_subdev *sd; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + decoder = kzalloc(sizeof(*decoder), GFP_KERNEL); + if (!decoder) + return -ENOMEM; + + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &saa7191_ops); + + err = saa7191_write_block(sd, sizeof(initseq), initseq); + if (err) { + printk(KERN_ERR "SAA7191 initialization failed\n"); + kfree(decoder); + return err; + } + + printk(KERN_INFO "SAA7191 initialized\n"); + + decoder->input = SAA7191_INPUT_COMPOSITE; + decoder->norm = V4L2_STD_PAL; + + err = saa7191_autodetect_norm(sd); + if (err && (err != -EBUSY)) + printk(KERN_ERR "SAA7191: Signal auto-detection failed\n"); + + return 0; +} + +static int saa7191_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_saa7191(sd)); + return 0; +} + +static const struct i2c_device_id saa7191_id[] = { + { "saa7191", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa7191_id); + +static struct i2c_driver saa7191_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "saa7191", + }, + .probe = saa7191_probe, + .remove = saa7191_remove, + .id_table = saa7191_id, +}; + +module_i2c_driver(saa7191_driver); diff --git a/drivers/media/i2c/saa7191.h b/drivers/media/i2c/saa7191.h new file mode 100644 index 00000000000..803c74d6066 --- /dev/null +++ b/drivers/media/i2c/saa7191.h @@ -0,0 +1,245 @@ +/* + * saa7191.h - Philips SAA7191 video decoder driver + * + * Copyright (C) 2003 Ladislav Michl + * Copyright (C) 2004,2005 Mikael Nousiainen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _SAA7191_H_ +#define _SAA7191_H_ + +/* Philips SAA7191 DMSD I2C bus address */ +#define SAA7191_ADDR 0x8a + +/* Register subaddresses. */ +#define SAA7191_REG_IDEL 0x00 +#define SAA7191_REG_HSYB 0x01 +#define SAA7191_REG_HSYS 0x02 +#define SAA7191_REG_HCLB 0x03 +#define SAA7191_REG_HCLS 0x04 +#define SAA7191_REG_HPHI 0x05 +#define SAA7191_REG_LUMA 0x06 +#define SAA7191_REG_HUEC 0x07 +#define SAA7191_REG_CKTQ 0x08 /* bits 3-7 */ +#define SAA7191_REG_CKTS 0x09 /* bits 3-7 */ +#define SAA7191_REG_PLSE 0x0a +#define SAA7191_REG_SESE 0x0b +#define SAA7191_REG_GAIN 0x0c +#define SAA7191_REG_STDC 0x0d +#define SAA7191_REG_IOCK 0x0e +#define SAA7191_REG_CTL3 0x0f +#define SAA7191_REG_CTL4 0x10 +#define SAA7191_REG_CHCV 0x11 +#define SAA7191_REG_HS6B 0x14 +#define SAA7191_REG_HS6S 0x15 +#define SAA7191_REG_HC6B 0x16 +#define SAA7191_REG_HC6S 0x17 +#define SAA7191_REG_HP6I 0x18 +#define SAA7191_REG_STATUS 0xff /* not really a subaddress */ + +/* Status Register definitions */ +#define SAA7191_STATUS_CODE 0x01 /* color detected flag */ +#define SAA7191_STATUS_FIDT 0x20 /* signal type 50/60 Hz */ +#define SAA7191_STATUS_HLCK 0x40 /* PLL unlocked(1)/locked(0) */ +#define SAA7191_STATUS_STTC 0x80 /* tv/vtr time constant */ + +/* Luminance Control Register definitions */ +/* input mode select bit: + * 0=CVBS (chrominance trap active), 1=S-Video (trap bypassed) */ +#define SAA7191_LUMA_BYPS 0x80 +/* pre-filter (only when chrominance trap is active) */ +#define SAA7191_LUMA_PREF 0x40 +/* aperture bandpass to select different characteristics with maximums + * (bits 4-5) */ +#define SAA7191_LUMA_BPSS_MASK 0x30 +#define SAA7191_LUMA_BPSS_SHIFT 4 +#define SAA7191_LUMA_BPSS_3 0x30 +#define SAA7191_LUMA_BPSS_2 0x20 +#define SAA7191_LUMA_BPSS_1 0x10 +#define SAA7191_LUMA_BPSS_0 0x00 +/* coring range for high frequency components according to 8-bit luminance + * (bits 2-3) + * 0=coring off, n= (+-)n LSB */ +#define SAA7191_LUMA_CORI_MASK 0x0c +#define SAA7191_LUMA_CORI_SHIFT 2 +#define SAA7191_LUMA_CORI_3 0x0c +#define SAA7191_LUMA_CORI_2 0x08 +#define SAA7191_LUMA_CORI_1 0x04 +#define SAA7191_LUMA_CORI_0 0x00 +/* aperture bandpass filter weights high frequency components of luminance + * signal (bits 0-1) + * 0=factor 0, 1=0.25, 2=0.5, 3=1 */ +#define SAA7191_LUMA_APER_MASK 0x03 +#define SAA7191_LUMA_APER_SHIFT 0 +#define SAA7191_LUMA_APER_3 0x03 +#define SAA7191_LUMA_APER_2 0x02 +#define SAA7191_LUMA_APER_1 0x01 +#define SAA7191_LUMA_APER_0 0x00 + +/* Chrominance Gain Control Settings Register definitions */ +/* colour on: 0=automatic colour-killer enabled, 1=forced colour on */ +#define SAA7191_GAIN_COLO 0x80 +/* chrominance gain control (AGC filter) + * 0=loop filter time constant slow, 1=medium, 2=fast, 3=actual gain */ +#define SAA7191_GAIN_LFIS_MASK 0x60 +#define SAA7191_GAIN_LFIS_SHIFT 5 +#define SAA7191_GAIN_LFIS_3 0x60 +#define SAA7191_GAIN_LFIS_2 0x40 +#define SAA7191_GAIN_LFIS_1 0x20 +#define SAA7191_GAIN_LFIS_0 0x00 + +/* Standard/Mode Control Register definitions */ +/* tv/vtr mode bit: 0=TV mode (slow time constant), + * 1=VTR mode (fast time constant) */ +#define SAA7191_STDC_VTRC 0x80 +/* SAA7191B-specific functions enable (RTCO, ODD and GPSW0 outputs) + * 0=outputs set to high-impedance (circuit equals SAA7191), 1=enabled */ +#define SAA7191_STDC_NFEN 0x08 +/* HREF generation: 0=like SAA7191, 1=HREF is 8xLLC2 clocks earlier */ +#define SAA7191_STDC_HRMV 0x04 +/* general purpose switch 0 + * (not used with VINO afaik) */ +#define SAA7191_STDC_GPSW0 0x02 +/* SECAM mode bit: 0=other standards, 1=SECAM */ +#define SAA7191_STDC_SECS 0x01 + +/* I/O and Clock Control Register definitions */ +/* horizontal clock PLL: 0=PLL closed, + * 1=PLL circuit open and horizontal freq fixed */ +#define SAA7191_IOCK_HPLL 0x80 +/* colour-difference output enable (outputs UV0-UV7) */ +#define SAA7191_IOCK_OEDC 0x40 +/* H-sync output enable */ +#define SAA7191_IOCK_OEHS 0x20 +/* V-sync output enable */ +#define SAA7191_IOCK_OEVS 0x10 +/* luminance output enable (outputs Y0-Y7) */ +#define SAA7191_IOCK_OEDY 0x08 +/* S-VHS bit (chrominance from CVBS or from chrominance input): + * 0=controlled by BYPS-bit, 1=from chrominance input */ +#define SAA7191_IOCK_CHRS 0x04 +/* general purpose switch 2 + * VINO-specific: 0=used with CVBS, 1=used with S-Video */ +#define SAA7191_IOCK_GPSW2 0x02 +/* general purpose switch 1 */ +/* VINO-specific: 0=always, 1=not used!*/ +#define SAA7191_IOCK_GPSW1 0x01 + +/* Miscellaneous Control #1 Register definitions */ +/* automatic field detection (50/60Hz standard) */ +#define SAA7191_CTL3_AUFD 0x80 +/* field select: (if AUFD=0) + * 0=50Hz (625 lines), 1=60Hz (525 lines) */ +#define SAA7191_CTL3_FSEL 0x40 +/* SECAM cross-colour reduction enable */ +#define SAA7191_CTL3_SXCR 0x20 +/* sync and clamping pulse enable (HCL and HSY outputs) */ +#define SAA7191_CTL3_SCEN 0x10 +/* output format: 0=4:1:1, 1=4:2:2 (4:2:2 for VINO) */ +#define SAA7191_CTL3_OFTS 0x08 +/* luminance delay compensation + * 0=0*2/LLC, 1=+1*2/LLC, 2=+2*2/LLC, 3=+3*2/LLC, + * 4=-4*2/LLC, 5=-3*2/LLC, 6=-2*2/LLC, 7=-1*2/LLC + * step size = 2/LLC = 67.8ns for 50Hz, 81.5ns for 60Hz */ +#define SAA7191_CTL3_YDEL_MASK 0x07 +#define SAA7191_CTL3_YDEL_SHIFT 0 +#define SAA7191_CTL3_YDEL2 0x04 +#define SAA7191_CTL3_YDEL1 0x02 +#define SAA7191_CTL3_YDEL0 0x01 + +/* Miscellaneous Control #2 Register definitions */ +/* select HREF position + * 0=normal, HREF is matched to YUV output port, + * 1=HREF is matched to CVBS input port */ +#define SAA7191_CTL4_HRFS 0x04 +/* vertical noise reduction + * 0=normal, 1=searching window, 2=auto-deflection, 3=reduction bypassed */ +#define SAA7191_CTL4_VNOI_MASK 0x03 +#define SAA7191_CTL4_VNOI_SHIFT 0 +#define SAA7191_CTL4_VNOI_3 0x03 +#define SAA7191_CTL4_VNOI_2 0x02 +#define SAA7191_CTL4_VNOI_1 0x01 +#define SAA7191_CTL4_VNOI_0 0x00 + +/* Chrominance Gain Control Register definitions + * - for QAM-modulated input signals, effects output amplitude + * (SECAM gain fixed) + * (nominal values for UV CCIR level) */ +#define SAA7191_CHCV_NTSC 0x2c +#define SAA7191_CHCV_PAL 0x59 + +/* Driver interface definitions */ +#define SAA7191_INPUT_COMPOSITE 0 +#define SAA7191_INPUT_SVIDEO 1 + +#define SAA7191_NORM_PAL 1 +#define SAA7191_NORM_NTSC 2 +#define SAA7191_NORM_SECAM 3 + +struct saa7191_status { + /* 0=no signal, 1=signal detected */ + int signal; + /* 0=50hz (pal) signal, 1=60hz (ntsc) signal */ + int signal_60hz; + /* 0=no color detected, 1=color detected */ + int color; + + /* current SAA7191_INPUT_ */ + int input; + /* current SAA7191_NORM_ */ + int norm; +}; + +#define SAA7191_BANDPASS_MIN 0x00 +#define SAA7191_BANDPASS_MAX 0x03 +#define SAA7191_BANDPASS_DEFAULT 0x00 + +#define SAA7191_BANDPASS_WEIGHT_MIN 0x00 +#define SAA7191_BANDPASS_WEIGHT_MAX 0x03 +#define SAA7191_BANDPASS_WEIGHT_DEFAULT 0x01 + +#define SAA7191_CORING_MIN 0x00 +#define SAA7191_CORING_MAX 0x03 +#define SAA7191_CORING_DEFAULT 0x00 + +#define SAA7191_HUE_MIN 0x00 +#define SAA7191_HUE_MAX 0xff +#define SAA7191_HUE_DEFAULT 0x80 + +#define SAA7191_VTRC_MIN 0x00 +#define SAA7191_VTRC_MAX 0x01 +#define SAA7191_VTRC_DEFAULT 0x00 + +#define SAA7191_FORCE_COLOUR_MIN 0x00 +#define SAA7191_FORCE_COLOUR_MAX 0x01 +#define SAA7191_FORCE_COLOUR_DEFAULT 0x00 + +#define SAA7191_CHROMA_GAIN_MIN 0x00 +#define SAA7191_CHROMA_GAIN_MAX 0x03 +#define SAA7191_CHROMA_GAIN_DEFAULT 0x00 + +#define SAA7191_LUMA_DELAY_MIN -0x04 +#define SAA7191_LUMA_DELAY_MAX 0x03 +#define SAA7191_LUMA_DELAY_DEFAULT 0x01 + +#define SAA7191_VNR_MIN 0x00 +#define SAA7191_VNR_MAX 0x03 +#define SAA7191_VNR_DEFAULT 0x00 + +#define SAA7191_CONTROL_BANDPASS (V4L2_CID_PRIVATE_BASE + 0) +#define SAA7191_CONTROL_BANDPASS_WEIGHT (V4L2_CID_PRIVATE_BASE + 1) +#define SAA7191_CONTROL_CORING (V4L2_CID_PRIVATE_BASE + 2) +#define SAA7191_CONTROL_FORCE_COLOUR (V4L2_CID_PRIVATE_BASE + 3) +#define SAA7191_CONTROL_CHROMA_GAIN (V4L2_CID_PRIVATE_BASE + 4) +#define SAA7191_CONTROL_VTRC (V4L2_CID_PRIVATE_BASE + 5) +#define SAA7191_CONTROL_LUMA_DELAY (V4L2_CID_PRIVATE_BASE + 6) +#define SAA7191_CONTROL_VNR (V4L2_CID_PRIVATE_BASE + 7) + +#define DECODER_SAA7191_GET_STATUS _IOR('d', 195, struct saa7191_status) +#define DECODER_SAA7191_SET_NORM _IOW('d', 196, int) + +#endif diff --git a/drivers/media/i2c/smiapp-pll.c b/drivers/media/i2c/smiapp-pll.c new file mode 100644 index 00000000000..a577614bd84 --- /dev/null +++ b/drivers/media/i2c/smiapp-pll.c @@ -0,0 +1,418 @@ +/* + * drivers/media/i2c/smiapp-pll.c + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include + +#include "smiapp-pll.h" + +/* Return an even number or one. */ +static inline uint32_t clk_div_even(uint32_t a) +{ + return max_t(uint32_t, 1, a & ~1); +} + +/* Return an even number or one. */ +static inline uint32_t clk_div_even_up(uint32_t a) +{ + if (a == 1) + return 1; + return (a + 1) & ~1; +} + +static inline uint32_t is_one_or_even(uint32_t a) +{ + if (a == 1) + return 1; + if (a & 1) + return 0; + + return 1; +} + +static int bounds_check(struct device *dev, uint32_t val, + uint32_t min, uint32_t max, char *str) +{ + if (val >= min && val <= max) + return 0; + + dev_warn(dev, "%s out of bounds: %d (%d--%d)\n", str, val, min, max); + + return -EINVAL; +} + +static void print_pll(struct device *dev, struct smiapp_pll *pll) +{ + dev_dbg(dev, "pre_pll_clk_div\t%d\n", pll->pre_pll_clk_div); + dev_dbg(dev, "pll_multiplier \t%d\n", pll->pll_multiplier); + if (pll->flags != SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { + dev_dbg(dev, "op_sys_clk_div \t%d\n", pll->op_sys_clk_div); + dev_dbg(dev, "op_pix_clk_div \t%d\n", pll->op_pix_clk_div); + } + dev_dbg(dev, "vt_sys_clk_div \t%d\n", pll->vt_sys_clk_div); + dev_dbg(dev, "vt_pix_clk_div \t%d\n", pll->vt_pix_clk_div); + + dev_dbg(dev, "ext_clk_freq_hz \t%d\n", pll->ext_clk_freq_hz); + dev_dbg(dev, "pll_ip_clk_freq_hz \t%d\n", pll->pll_ip_clk_freq_hz); + dev_dbg(dev, "pll_op_clk_freq_hz \t%d\n", pll->pll_op_clk_freq_hz); + if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { + dev_dbg(dev, "op_sys_clk_freq_hz \t%d\n", + pll->op_sys_clk_freq_hz); + dev_dbg(dev, "op_pix_clk_freq_hz \t%d\n", + pll->op_pix_clk_freq_hz); + } + dev_dbg(dev, "vt_sys_clk_freq_hz \t%d\n", pll->vt_sys_clk_freq_hz); + dev_dbg(dev, "vt_pix_clk_freq_hz \t%d\n", pll->vt_pix_clk_freq_hz); +} + +int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits, + struct smiapp_pll *pll) +{ + uint32_t sys_div; + uint32_t best_pix_div = INT_MAX >> 1; + uint32_t vt_op_binning_div; + uint32_t lane_op_clock_ratio; + uint32_t mul, div; + uint32_t more_mul_min, more_mul_max; + uint32_t more_mul_factor; + uint32_t min_vt_div, max_vt_div, vt_div; + uint32_t min_sys_div, max_sys_div; + unsigned int i; + int rval; + + if (pll->flags & SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE) + lane_op_clock_ratio = pll->lanes; + else + lane_op_clock_ratio = 1; + dev_dbg(dev, "lane_op_clock_ratio: %d\n", lane_op_clock_ratio); + + dev_dbg(dev, "binning: %dx%d\n", pll->binning_horizontal, + pll->binning_vertical); + + /* CSI transfers 2 bits per clock per lane; thus times 2 */ + pll->pll_op_clk_freq_hz = pll->link_freq * 2 + * (pll->lanes / lane_op_clock_ratio); + + /* Figure out limits for pre-pll divider based on extclk */ + dev_dbg(dev, "min / max pre_pll_clk_div: %d / %d\n", + limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); + limits->max_pre_pll_clk_div = + min_t(uint16_t, limits->max_pre_pll_clk_div, + clk_div_even(pll->ext_clk_freq_hz / + limits->min_pll_ip_freq_hz)); + limits->min_pre_pll_clk_div = + max_t(uint16_t, limits->min_pre_pll_clk_div, + clk_div_even_up( + DIV_ROUND_UP(pll->ext_clk_freq_hz, + limits->max_pll_ip_freq_hz))); + dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %d / %d\n", + limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); + + i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz); + mul = div_u64(pll->pll_op_clk_freq_hz, i); + div = pll->ext_clk_freq_hz / i; + dev_dbg(dev, "mul %d / div %d\n", mul, div); + + limits->min_pre_pll_clk_div = + max_t(uint16_t, limits->min_pre_pll_clk_div, + clk_div_even_up( + DIV_ROUND_UP(mul * pll->ext_clk_freq_hz, + limits->max_pll_op_freq_hz))); + dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %d / %d\n", + limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); + + if (limits->min_pre_pll_clk_div > limits->max_pre_pll_clk_div) { + dev_err(dev, "unable to compute pre_pll divisor\n"); + return -EINVAL; + } + + pll->pre_pll_clk_div = limits->min_pre_pll_clk_div; + + /* + * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be + * too high. + */ + dev_dbg(dev, "pre_pll_clk_div %d\n", pll->pre_pll_clk_div); + + /* Don't go above max pll multiplier. */ + more_mul_max = limits->max_pll_multiplier / mul; + dev_dbg(dev, "more_mul_max: max_pll_multiplier check: %d\n", + more_mul_max); + /* Don't go above max pll op frequency. */ + more_mul_max = + min_t(int, + more_mul_max, + limits->max_pll_op_freq_hz + / (pll->ext_clk_freq_hz / pll->pre_pll_clk_div * mul)); + dev_dbg(dev, "more_mul_max: max_pll_op_freq_hz check: %d\n", + more_mul_max); + /* Don't go above the division capability of op sys clock divider. */ + more_mul_max = min(more_mul_max, + limits->max_op_sys_clk_div * pll->pre_pll_clk_div + / div); + dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %d\n", + more_mul_max); + /* Ensure we won't go above min_pll_multiplier. */ + more_mul_max = min(more_mul_max, + DIV_ROUND_UP(limits->max_pll_multiplier, mul)); + dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %d\n", + more_mul_max); + + /* Ensure we won't go below min_pll_op_freq_hz. */ + more_mul_min = DIV_ROUND_UP(limits->min_pll_op_freq_hz, + pll->ext_clk_freq_hz / pll->pre_pll_clk_div + * mul); + dev_dbg(dev, "more_mul_min: min_pll_op_freq_hz check: %d\n", + more_mul_min); + /* Ensure we won't go below min_pll_multiplier. */ + more_mul_min = max(more_mul_min, + DIV_ROUND_UP(limits->min_pll_multiplier, mul)); + dev_dbg(dev, "more_mul_min: min_pll_multiplier check: %d\n", + more_mul_min); + + if (more_mul_min > more_mul_max) { + dev_warn(dev, + "unable to compute more_mul_min and more_mul_max"); + return -EINVAL; + } + + more_mul_factor = lcm(div, pll->pre_pll_clk_div) / div; + dev_dbg(dev, "more_mul_factor: %d\n", more_mul_factor); + more_mul_factor = lcm(more_mul_factor, limits->min_op_sys_clk_div); + dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n", + more_mul_factor); + i = roundup(more_mul_min, more_mul_factor); + if (!is_one_or_even(i)) + i <<= 1; + + dev_dbg(dev, "final more_mul: %d\n", i); + if (i > more_mul_max) { + dev_warn(dev, "final more_mul is bad, max %d", more_mul_max); + return -EINVAL; + } + + pll->pll_multiplier = mul * i; + pll->op_sys_clk_div = div * i / pll->pre_pll_clk_div; + dev_dbg(dev, "op_sys_clk_div: %d\n", pll->op_sys_clk_div); + + pll->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz + / pll->pre_pll_clk_div; + + pll->pll_op_clk_freq_hz = pll->pll_ip_clk_freq_hz + * pll->pll_multiplier; + + /* Derive pll_op_clk_freq_hz. */ + pll->op_sys_clk_freq_hz = + pll->pll_op_clk_freq_hz / pll->op_sys_clk_div; + + pll->op_pix_clk_div = pll->bits_per_pixel; + dev_dbg(dev, "op_pix_clk_div: %d\n", pll->op_pix_clk_div); + + pll->op_pix_clk_freq_hz = + pll->op_sys_clk_freq_hz / pll->op_pix_clk_div; + + /* + * Some sensors perform analogue binning and some do this + * digitally. The ones doing this digitally can be roughly be + * found out using this formula. The ones doing this digitally + * should run at higher clock rate, so smaller divisor is used + * on video timing side. + */ + if (limits->min_line_length_pck_bin > limits->min_line_length_pck + / pll->binning_horizontal) + vt_op_binning_div = pll->binning_horizontal; + else + vt_op_binning_div = 1; + dev_dbg(dev, "vt_op_binning_div: %d\n", vt_op_binning_div); + + /* + * Profile 2 supports vt_pix_clk_div E [4, 10] + * + * Horizontal binning can be used as a base for difference in + * divisors. One must make sure that horizontal blanking is + * enough to accommodate the CSI-2 sync codes. + * + * Take scaling factor into account as well. + * + * Find absolute limits for the factor of vt divider. + */ + dev_dbg(dev, "scale_m: %d\n", pll->scale_m); + min_vt_div = DIV_ROUND_UP(pll->op_pix_clk_div * pll->op_sys_clk_div + * pll->scale_n, + lane_op_clock_ratio * vt_op_binning_div + * pll->scale_m); + + /* Find smallest and biggest allowed vt divisor. */ + dev_dbg(dev, "min_vt_div: %d\n", min_vt_div); + min_vt_div = max(min_vt_div, + DIV_ROUND_UP(pll->pll_op_clk_freq_hz, + limits->max_vt_pix_clk_freq_hz)); + dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %d\n", + min_vt_div); + min_vt_div = max_t(uint32_t, min_vt_div, + limits->min_vt_pix_clk_div + * limits->min_vt_sys_clk_div); + dev_dbg(dev, "min_vt_div: min_vt_clk_div: %d\n", min_vt_div); + + max_vt_div = limits->max_vt_sys_clk_div * limits->max_vt_pix_clk_div; + dev_dbg(dev, "max_vt_div: %d\n", max_vt_div); + max_vt_div = min(max_vt_div, + DIV_ROUND_UP(pll->pll_op_clk_freq_hz, + limits->min_vt_pix_clk_freq_hz)); + dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %d\n", + max_vt_div); + + /* + * Find limitsits for sys_clk_div. Not all values are possible + * with all values of pix_clk_div. + */ + min_sys_div = limits->min_vt_sys_clk_div; + dev_dbg(dev, "min_sys_div: %d\n", min_sys_div); + min_sys_div = max(min_sys_div, + DIV_ROUND_UP(min_vt_div, + limits->max_vt_pix_clk_div)); + dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %d\n", min_sys_div); + min_sys_div = max(min_sys_div, + pll->pll_op_clk_freq_hz + / limits->max_vt_sys_clk_freq_hz); + dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %d\n", min_sys_div); + min_sys_div = clk_div_even_up(min_sys_div); + dev_dbg(dev, "min_sys_div: one or even: %d\n", min_sys_div); + + max_sys_div = limits->max_vt_sys_clk_div; + dev_dbg(dev, "max_sys_div: %d\n", max_sys_div); + max_sys_div = min(max_sys_div, + DIV_ROUND_UP(max_vt_div, + limits->min_vt_pix_clk_div)); + dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %d\n", max_sys_div); + max_sys_div = min(max_sys_div, + DIV_ROUND_UP(pll->pll_op_clk_freq_hz, + limits->min_vt_pix_clk_freq_hz)); + dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %d\n", max_sys_div); + + /* + * Find pix_div such that a legal pix_div * sys_div results + * into a value which is not smaller than div, the desired + * divisor. + */ + for (vt_div = min_vt_div; vt_div <= max_vt_div; + vt_div += 2 - (vt_div & 1)) { + for (sys_div = min_sys_div; + sys_div <= max_sys_div; + sys_div += 2 - (sys_div & 1)) { + int pix_div = DIV_ROUND_UP(vt_div, sys_div); + + if (pix_div < limits->min_vt_pix_clk_div + || pix_div > limits->max_vt_pix_clk_div) { + dev_dbg(dev, + "pix_div %d too small or too big (%d--%d)\n", + pix_div, + limits->min_vt_pix_clk_div, + limits->max_vt_pix_clk_div); + continue; + } + + /* Check if this one is better. */ + if (pix_div * sys_div + <= roundup(min_vt_div, best_pix_div)) + best_pix_div = pix_div; + } + if (best_pix_div < INT_MAX >> 1) + break; + } + + pll->vt_sys_clk_div = DIV_ROUND_UP(min_vt_div, best_pix_div); + pll->vt_pix_clk_div = best_pix_div; + + pll->vt_sys_clk_freq_hz = + pll->pll_op_clk_freq_hz / pll->vt_sys_clk_div; + pll->vt_pix_clk_freq_hz = + pll->vt_sys_clk_freq_hz / pll->vt_pix_clk_div; + + pll->pixel_rate_csi = + pll->op_pix_clk_freq_hz * lane_op_clock_ratio; + + print_pll(dev, pll); + + rval = bounds_check(dev, pll->pre_pll_clk_div, + limits->min_pre_pll_clk_div, + limits->max_pre_pll_clk_div, "pre_pll_clk_div"); + if (!rval) + rval = bounds_check( + dev, pll->pll_ip_clk_freq_hz, + limits->min_pll_ip_freq_hz, limits->max_pll_ip_freq_hz, + "pll_ip_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->pll_multiplier, + limits->min_pll_multiplier, limits->max_pll_multiplier, + "pll_multiplier"); + if (!rval) + rval = bounds_check( + dev, pll->pll_op_clk_freq_hz, + limits->min_pll_op_freq_hz, limits->max_pll_op_freq_hz, + "pll_op_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->op_sys_clk_div, + limits->min_op_sys_clk_div, limits->max_op_sys_clk_div, + "op_sys_clk_div"); + if (!rval) + rval = bounds_check( + dev, pll->op_pix_clk_div, + limits->min_op_pix_clk_div, limits->max_op_pix_clk_div, + "op_pix_clk_div"); + if (!rval) + rval = bounds_check( + dev, pll->op_sys_clk_freq_hz, + limits->min_op_sys_clk_freq_hz, + limits->max_op_sys_clk_freq_hz, + "op_sys_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->op_pix_clk_freq_hz, + limits->min_op_pix_clk_freq_hz, + limits->max_op_pix_clk_freq_hz, + "op_pix_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->vt_sys_clk_freq_hz, + limits->min_vt_sys_clk_freq_hz, + limits->max_vt_sys_clk_freq_hz, + "vt_sys_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->vt_pix_clk_freq_hz, + limits->min_vt_pix_clk_freq_hz, + limits->max_vt_pix_clk_freq_hz, + "vt_pix_clk_freq_hz"); + + return rval; +} +EXPORT_SYMBOL_GPL(smiapp_pll_calculate); + +MODULE_AUTHOR("Sakari Ailus "); +MODULE_DESCRIPTION("Generic SMIA/SMIA++ PLL calculator"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/smiapp-pll.h b/drivers/media/i2c/smiapp-pll.h new file mode 100644 index 00000000000..cb2d2db5d02 --- /dev/null +++ b/drivers/media/i2c/smiapp-pll.h @@ -0,0 +1,103 @@ +/* + * drivers/media/i2c/smiapp-pll.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef SMIAPP_PLL_H +#define SMIAPP_PLL_H + +#include + +struct smiapp_pll { + uint8_t lanes; + uint8_t binning_horizontal; + uint8_t binning_vertical; + uint8_t scale_m; + uint8_t scale_n; + uint8_t bits_per_pixel; + uint16_t flags; + uint32_t link_freq; + + uint16_t pre_pll_clk_div; + uint16_t pll_multiplier; + uint16_t op_sys_clk_div; + uint16_t op_pix_clk_div; + uint16_t vt_sys_clk_div; + uint16_t vt_pix_clk_div; + + uint32_t ext_clk_freq_hz; + uint32_t pll_ip_clk_freq_hz; + uint32_t pll_op_clk_freq_hz; + uint32_t op_sys_clk_freq_hz; + uint32_t op_pix_clk_freq_hz; + uint32_t vt_sys_clk_freq_hz; + uint32_t vt_pix_clk_freq_hz; + + uint32_t pixel_rate_csi; +}; + +struct smiapp_pll_limits { + /* Strict PLL limits */ + uint32_t min_ext_clk_freq_hz; + uint32_t max_ext_clk_freq_hz; + uint16_t min_pre_pll_clk_div; + uint16_t max_pre_pll_clk_div; + uint32_t min_pll_ip_freq_hz; + uint32_t max_pll_ip_freq_hz; + uint16_t min_pll_multiplier; + uint16_t max_pll_multiplier; + uint32_t min_pll_op_freq_hz; + uint32_t max_pll_op_freq_hz; + + uint16_t min_vt_sys_clk_div; + uint16_t max_vt_sys_clk_div; + uint32_t min_vt_sys_clk_freq_hz; + uint32_t max_vt_sys_clk_freq_hz; + uint16_t min_vt_pix_clk_div; + uint16_t max_vt_pix_clk_div; + uint32_t min_vt_pix_clk_freq_hz; + uint32_t max_vt_pix_clk_freq_hz; + + uint16_t min_op_sys_clk_div; + uint16_t max_op_sys_clk_div; + uint32_t min_op_sys_clk_freq_hz; + uint32_t max_op_sys_clk_freq_hz; + uint16_t min_op_pix_clk_div; + uint16_t max_op_pix_clk_div; + uint32_t min_op_pix_clk_freq_hz; + uint32_t max_op_pix_clk_freq_hz; + + /* Other relevant limits */ + uint32_t min_line_length_pck_bin; + uint32_t min_line_length_pck; +}; + +/* op pix clock is for all lanes in total normally */ +#define SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0) +#define SMIAPP_PLL_FLAG_NO_OP_CLOCKS (1 << 1) + +struct device; + +int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits, + struct smiapp_pll *pll); + +#endif /* SMIAPP_PLL_H */ diff --git a/drivers/media/i2c/smiapp/Kconfig b/drivers/media/i2c/smiapp/Kconfig new file mode 100644 index 00000000000..3149cda1d0d --- /dev/null +++ b/drivers/media/i2c/smiapp/Kconfig @@ -0,0 +1,7 @@ +config VIDEO_SMIAPP + tristate "SMIA++/SMIA sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAVE_CLK + depends on MEDIA_CAMERA_SUPPORT + select VIDEO_SMIAPP_PLL + ---help--- + This is a generic driver for SMIA++/SMIA camera modules. diff --git a/drivers/media/i2c/smiapp/Makefile b/drivers/media/i2c/smiapp/Makefile new file mode 100644 index 00000000000..f45a003cbe7 --- /dev/null +++ b/drivers/media/i2c/smiapp/Makefile @@ -0,0 +1,5 @@ +smiapp-objs += smiapp-core.o smiapp-regs.o \ + smiapp-quirk.o smiapp-limits.o +obj-$(CONFIG_VIDEO_SMIAPP) += smiapp.o + +ccflags-y += -Idrivers/media/i2c diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c new file mode 100644 index 00000000000..1cf914d1134 --- /dev/null +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -0,0 +1,2895 @@ +/* + * drivers/media/i2c/smiapp/smiapp-core.c + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2010--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * Based on smiapp driver by Vimarsh Zutshi + * Based on jt8ev1.c by Vimarsh Zutshi + * Based on smia-sensor.c by Tuukka Toivonen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "smiapp.h" + +#define SMIAPP_ALIGN_DIM(dim, flags) \ + ((flags) & V4L2_SEL_FLAG_GE \ + ? ALIGN((dim), 2) \ + : (dim) & ~1) + +/* + * smiapp_module_idents - supported camera modules + */ +static const struct smiapp_module_ident smiapp_module_idents[] = { + SMIAPP_IDENT_L(0x01, 0x022b, -1, "vs6555"), + SMIAPP_IDENT_L(0x01, 0x022e, -1, "vw6558"), + SMIAPP_IDENT_L(0x07, 0x7698, -1, "ovm7698"), + SMIAPP_IDENT_L(0x0b, 0x4242, -1, "smiapp-003"), + SMIAPP_IDENT_L(0x0c, 0x208a, -1, "tcm8330md"), + SMIAPP_IDENT_LQ(0x0c, 0x2134, -1, "tcm8500md", &smiapp_tcm8500md_quirk), + SMIAPP_IDENT_L(0x0c, 0x213e, -1, "et8en2"), + SMIAPP_IDENT_L(0x0c, 0x2184, -1, "tcm8580md"), + SMIAPP_IDENT_LQ(0x0c, 0x560f, -1, "jt8ew9", &smiapp_jt8ew9_quirk), + SMIAPP_IDENT_LQ(0x10, 0x4141, -1, "jt8ev1", &smiapp_jt8ev1_quirk), + SMIAPP_IDENT_LQ(0x10, 0x4241, -1, "imx125es", &smiapp_imx125es_quirk), +}; + +/* + * + * Dynamic Capability Identification + * + */ + +static int smiapp_read_frame_fmt(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + u32 fmt_model_type, fmt_model_subtype, ncol_desc, nrow_desc; + unsigned int i; + int rval; + int line_count = 0; + int embedded_start = -1, embedded_end = -1; + int image_start = 0; + + rval = smiapp_read(sensor, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE, + &fmt_model_type); + if (rval) + return rval; + + rval = smiapp_read(sensor, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE, + &fmt_model_subtype); + if (rval) + return rval; + + ncol_desc = (fmt_model_subtype + & SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_MASK) + >> SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_SHIFT; + nrow_desc = fmt_model_subtype + & SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NROWS_MASK; + + dev_dbg(&client->dev, "format_model_type %s\n", + fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE + ? "2 byte" : + fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE + ? "4 byte" : "is simply bad"); + + for (i = 0; i < ncol_desc + nrow_desc; i++) { + u32 desc; + u32 pixelcode; + u32 pixels; + char *which; + char *what; + + if (fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE) { + rval = smiapp_read( + sensor, + SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(i), + &desc); + if (rval) + return rval; + + pixelcode = + (desc + & SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_MASK) + >> SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_SHIFT; + pixels = desc & SMIAPP_FRAME_FORMAT_DESC_2_PIXELS_MASK; + } else if (fmt_model_type + == SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE) { + rval = smiapp_read( + sensor, + SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(i), + &desc); + if (rval) + return rval; + + pixelcode = + (desc + & SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_MASK) + >> SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_SHIFT; + pixels = desc & SMIAPP_FRAME_FORMAT_DESC_4_PIXELS_MASK; + } else { + dev_dbg(&client->dev, + "invalid frame format model type %d\n", + fmt_model_type); + return -EINVAL; + } + + if (i < ncol_desc) + which = "columns"; + else + which = "rows"; + + switch (pixelcode) { + case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED: + what = "embedded"; + break; + case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DUMMY: + what = "dummy"; + break; + case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_BLACK: + what = "black"; + break; + case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DARK: + what = "dark"; + break; + case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE: + what = "visible"; + break; + default: + what = "invalid"; + dev_dbg(&client->dev, "pixelcode %d\n", pixelcode); + break; + } + + dev_dbg(&client->dev, "%s pixels: %d %s\n", + what, pixels, which); + + if (i < ncol_desc) + continue; + + /* Handle row descriptors */ + if (pixelcode + == SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED) { + embedded_start = line_count; + } else { + if (pixelcode == SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE + || pixels >= sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES] / 2) + image_start = line_count; + if (embedded_start != -1 && embedded_end == -1) + embedded_end = line_count; + } + line_count += pixels; + } + + if (embedded_start == -1 || embedded_end == -1) { + embedded_start = 0; + embedded_end = 0; + } + + dev_dbg(&client->dev, "embedded data from lines %d to %d\n", + embedded_start, embedded_end); + dev_dbg(&client->dev, "image data starts at line %d\n", image_start); + + return 0; +} + +static int smiapp_pll_configure(struct smiapp_sensor *sensor) +{ + struct smiapp_pll *pll = &sensor->pll; + int rval; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_VT_PIX_CLK_DIV, pll->vt_pix_clk_div); + if (rval < 0) + return rval; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_VT_SYS_CLK_DIV, pll->vt_sys_clk_div); + if (rval < 0) + return rval; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_PRE_PLL_CLK_DIV, pll->pre_pll_clk_div); + if (rval < 0) + return rval; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_PLL_MULTIPLIER, pll->pll_multiplier); + if (rval < 0) + return rval; + + /* Lane op clock ratio does not apply here. */ + rval = smiapp_write( + sensor, SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS, + DIV_ROUND_UP(pll->op_sys_clk_freq_hz, 1000000 / 256 / 256)); + if (rval < 0 || sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) + return rval; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_OP_PIX_CLK_DIV, pll->op_pix_clk_div); + if (rval < 0) + return rval; + + return smiapp_write( + sensor, SMIAPP_REG_U16_OP_SYS_CLK_DIV, pll->op_sys_clk_div); +} + +static int smiapp_pll_update(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct smiapp_pll_limits lim = { + .min_pre_pll_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_PRE_PLL_CLK_DIV], + .max_pre_pll_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_PRE_PLL_CLK_DIV], + .min_pll_ip_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ], + .max_pll_ip_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_PLL_IP_FREQ_HZ], + .min_pll_multiplier = sensor->limits[SMIAPP_LIMIT_MIN_PLL_MULTIPLIER], + .max_pll_multiplier = sensor->limits[SMIAPP_LIMIT_MAX_PLL_MULTIPLIER], + .min_pll_op_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_PLL_OP_FREQ_HZ], + .max_pll_op_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_PLL_OP_FREQ_HZ], + + .min_op_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV], + .max_op_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV], + .min_op_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV], + .max_op_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV], + .min_op_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_FREQ_HZ], + .max_op_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_FREQ_HZ], + .min_op_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_FREQ_HZ], + .max_op_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_FREQ_HZ], + + .min_vt_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_DIV], + .max_vt_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_DIV], + .min_vt_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_DIV], + .max_vt_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_DIV], + .min_vt_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_FREQ_HZ], + .max_vt_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_FREQ_HZ], + .min_vt_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_FREQ_HZ], + .max_vt_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_FREQ_HZ], + + .min_line_length_pck_bin = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN], + .min_line_length_pck = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK], + }; + struct smiapp_pll *pll = &sensor->pll; + int rval; + + memset(&sensor->pll, 0, sizeof(sensor->pll)); + + pll->lanes = sensor->platform_data->lanes; + pll->ext_clk_freq_hz = sensor->platform_data->ext_clk; + + if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) { + /* + * Fill in operational clock divisors limits from the + * video timing ones. On profile 0 sensors the + * requirements regarding them are essentially the + * same as on VT ones. + */ + lim.min_op_sys_clk_div = lim.min_vt_sys_clk_div; + lim.max_op_sys_clk_div = lim.max_vt_sys_clk_div; + lim.min_op_pix_clk_div = lim.min_vt_pix_clk_div; + lim.max_op_pix_clk_div = lim.max_vt_pix_clk_div; + lim.min_op_sys_clk_freq_hz = lim.min_vt_sys_clk_freq_hz; + lim.max_op_sys_clk_freq_hz = lim.max_vt_sys_clk_freq_hz; + lim.min_op_pix_clk_freq_hz = lim.min_vt_pix_clk_freq_hz; + lim.max_op_pix_clk_freq_hz = lim.max_vt_pix_clk_freq_hz; + /* Profile 0 sensors have no separate OP clock branch. */ + pll->flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS; + } + + if (smiapp_needs_quirk(sensor, + SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE)) + pll->flags |= SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE; + + pll->binning_horizontal = sensor->binning_horizontal; + pll->binning_vertical = sensor->binning_vertical; + pll->link_freq = + sensor->link_freq->qmenu_int[sensor->link_freq->val]; + pll->scale_m = sensor->scale_m; + pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; + pll->bits_per_pixel = sensor->csi_format->compressed; + + rval = smiapp_pll_calculate(&client->dev, &lim, pll); + if (rval < 0) + return rval; + + sensor->pixel_rate_parray->cur.val64 = pll->vt_pix_clk_freq_hz; + sensor->pixel_rate_csi->cur.val64 = pll->pixel_rate_csi; + + return 0; +} + + +/* + * + * V4L2 Controls handling + * + */ + +static void __smiapp_update_exposure_limits(struct smiapp_sensor *sensor) +{ + struct v4l2_ctrl *ctrl = sensor->exposure; + int max; + + max = sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height + + sensor->vblank->val + - sensor->limits[SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MAX_MARGIN]; + + ctrl->maximum = max; + if (ctrl->default_value > max) + ctrl->default_value = max; + if (ctrl->val > max) + ctrl->val = max; + if (ctrl->cur.val > max) + ctrl->cur.val = max; +} + +/* + * Order matters. + * + * 1. Bits-per-pixel, descending. + * 2. Bits-per-pixel compressed, descending. + * 3. Pixel order, same as in pixel_order_str. Formats for all four pixel + * orders must be defined. + */ +static const struct smiapp_csi_data_format smiapp_csi_data_formats[] = { + { V4L2_MBUS_FMT_SGRBG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GRBG, }, + { V4L2_MBUS_FMT_SRGGB12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_RGGB, }, + { V4L2_MBUS_FMT_SBGGR12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_BGGR, }, + { V4L2_MBUS_FMT_SGBRG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GBRG, }, + { V4L2_MBUS_FMT_SGRBG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GRBG, }, + { V4L2_MBUS_FMT_SRGGB10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_RGGB, }, + { V4L2_MBUS_FMT_SBGGR10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_BGGR, }, + { V4L2_MBUS_FMT_SGBRG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GBRG, }, + { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GRBG, }, + { V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_RGGB, }, + { V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_BGGR, }, + { V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GBRG, }, + { V4L2_MBUS_FMT_SGRBG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GRBG, }, + { V4L2_MBUS_FMT_SRGGB8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_RGGB, }, + { V4L2_MBUS_FMT_SBGGR8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_BGGR, }, + { V4L2_MBUS_FMT_SGBRG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GBRG, }, +}; + +const char *pixel_order_str[] = { "GRBG", "RGGB", "BGGR", "GBRG" }; + +#define to_csi_format_idx(fmt) (((unsigned long)(fmt) \ + - (unsigned long)smiapp_csi_data_formats) \ + / sizeof(*smiapp_csi_data_formats)) + +static u32 smiapp_pixel_order(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int flip = 0; + + if (sensor->hflip) { + if (sensor->hflip->val) + flip |= SMIAPP_IMAGE_ORIENTATION_HFLIP; + + if (sensor->vflip->val) + flip |= SMIAPP_IMAGE_ORIENTATION_VFLIP; + } + + flip ^= sensor->hvflip_inv_mask; + + dev_dbg(&client->dev, "flip %d\n", flip); + return sensor->default_pixel_order ^ flip; +} + +static void smiapp_update_mbus_formats(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int csi_format_idx = + to_csi_format_idx(sensor->csi_format) & ~3; + unsigned int internal_csi_format_idx = + to_csi_format_idx(sensor->internal_csi_format) & ~3; + unsigned int pixel_order = smiapp_pixel_order(sensor); + + sensor->mbus_frame_fmts = + sensor->default_mbus_frame_fmts << pixel_order; + sensor->csi_format = + &smiapp_csi_data_formats[csi_format_idx + pixel_order]; + sensor->internal_csi_format = + &smiapp_csi_data_formats[internal_csi_format_idx + + pixel_order]; + + BUG_ON(max(internal_csi_format_idx, csi_format_idx) + pixel_order + >= ARRAY_SIZE(smiapp_csi_data_formats)); + BUG_ON(min(internal_csi_format_idx, csi_format_idx) < 0); + + dev_dbg(&client->dev, "new pixel order %s\n", + pixel_order_str[pixel_order]); +} + +static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct smiapp_sensor *sensor = + container_of(ctrl->handler, struct smiapp_subdev, ctrl_handler) + ->sensor; + u32 orient = 0; + int exposure; + int rval; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + return smiapp_write( + sensor, + SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL, ctrl->val); + + case V4L2_CID_EXPOSURE: + return smiapp_write( + sensor, + SMIAPP_REG_U16_COARSE_INTEGRATION_TIME, ctrl->val); + + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + if (sensor->streaming) + return -EBUSY; + + if (sensor->hflip->val) + orient |= SMIAPP_IMAGE_ORIENTATION_HFLIP; + + if (sensor->vflip->val) + orient |= SMIAPP_IMAGE_ORIENTATION_VFLIP; + + orient ^= sensor->hvflip_inv_mask; + rval = smiapp_write(sensor, + SMIAPP_REG_U8_IMAGE_ORIENTATION, + orient); + if (rval < 0) + return rval; + + smiapp_update_mbus_formats(sensor); + + return 0; + + case V4L2_CID_VBLANK: + exposure = sensor->exposure->val; + + __smiapp_update_exposure_limits(sensor); + + if (exposure > sensor->exposure->maximum) { + sensor->exposure->val = + sensor->exposure->maximum; + rval = smiapp_set_ctrl( + sensor->exposure); + if (rval < 0) + return rval; + } + + return smiapp_write( + sensor, SMIAPP_REG_U16_FRAME_LENGTH_LINES, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height + + ctrl->val); + + case V4L2_CID_HBLANK: + return smiapp_write( + sensor, SMIAPP_REG_U16_LINE_LENGTH_PCK, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width + + ctrl->val); + + case V4L2_CID_LINK_FREQ: + if (sensor->streaming) + return -EBUSY; + + return smiapp_pll_update(sensor); + + default: + return -EINVAL; + } +} + +static const struct v4l2_ctrl_ops smiapp_ctrl_ops = { + .s_ctrl = smiapp_set_ctrl, +}; + +static int smiapp_init_controls(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int max; + int rval; + + rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 7); + if (rval) + return rval; + sensor->pixel_array->ctrl_handler.lock = &sensor->mutex; + + sensor->analog_gain = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, + sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN], + sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX], + max(sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_STEP], 1U), + sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN]); + + /* Exposure limits will be updated soon, use just something here. */ + sensor->exposure = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 0, 1, 0); + + sensor->hflip = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + sensor->vflip = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + sensor->vblank = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_VBLANK, 0, 1, 1, 0); + + if (sensor->vblank) + sensor->vblank->flags |= V4L2_CTRL_FLAG_UPDATE; + + sensor->hblank = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_HBLANK, 0, 1, 1, 0); + + if (sensor->hblank) + sensor->hblank->flags |= V4L2_CTRL_FLAG_UPDATE; + + sensor->pixel_rate_parray = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, 0, 1, 0); + + if (sensor->pixel_array->ctrl_handler.error) { + dev_err(&client->dev, + "pixel array controls initialization failed (%d)\n", + sensor->pixel_array->ctrl_handler.error); + rval = sensor->pixel_array->ctrl_handler.error; + goto error; + } + + sensor->pixel_array->sd.ctrl_handler = + &sensor->pixel_array->ctrl_handler; + + v4l2_ctrl_cluster(2, &sensor->hflip); + + rval = v4l2_ctrl_handler_init(&sensor->src->ctrl_handler, 0); + if (rval) + goto error; + sensor->src->ctrl_handler.lock = &sensor->mutex; + + for (max = 0; sensor->platform_data->op_sys_clock[max + 1]; max++); + + sensor->link_freq = v4l2_ctrl_new_int_menu( + &sensor->src->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_LINK_FREQ, max, 0, + sensor->platform_data->op_sys_clock); + + sensor->pixel_rate_csi = v4l2_ctrl_new_std( + &sensor->src->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, 0, 1, 0); + + if (sensor->src->ctrl_handler.error) { + dev_err(&client->dev, + "src controls initialization failed (%d)\n", + sensor->src->ctrl_handler.error); + rval = sensor->src->ctrl_handler.error; + goto error; + } + + sensor->src->sd.ctrl_handler = + &sensor->src->ctrl_handler; + + return 0; + +error: + v4l2_ctrl_handler_free(&sensor->pixel_array->ctrl_handler); + v4l2_ctrl_handler_free(&sensor->src->ctrl_handler); + + return rval; +} + +static void smiapp_free_controls(struct smiapp_sensor *sensor) +{ + unsigned int i; + + for (i = 0; i < sensor->ssds_used; i++) + v4l2_ctrl_handler_free(&sensor->ssds[i].ctrl_handler); +} + +static int smiapp_get_limits(struct smiapp_sensor *sensor, int const *limit, + unsigned int n) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int i; + u32 val; + int rval; + + for (i = 0; i < n; i++) { + rval = smiapp_read( + sensor, smiapp_reg_limits[limit[i]].addr, &val); + if (rval) + return rval; + sensor->limits[limit[i]] = val; + dev_dbg(&client->dev, "0x%8.8x \"%s\" = %d, 0x%x\n", + smiapp_reg_limits[limit[i]].addr, + smiapp_reg_limits[limit[i]].what, val, val); + } + + return 0; +} + +static int smiapp_get_all_limits(struct smiapp_sensor *sensor) +{ + unsigned int i; + int rval; + + for (i = 0; i < SMIAPP_LIMIT_LAST; i++) { + rval = smiapp_get_limits(sensor, &i, 1); + if (rval < 0) + return rval; + } + + if (sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] == 0) + smiapp_replace_limit(sensor, SMIAPP_LIMIT_SCALER_N_MIN, 16); + + return 0; +} + +static int smiapp_get_limits_binning(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + static u32 const limits[] = { + SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN, + SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN, + SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN, + SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN, + SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN, + SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN_BIN, + SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN, + }; + static u32 const limits_replace[] = { + SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES, + SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES, + SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK, + SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK, + SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK, + SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN, + SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN, + }; + unsigned int i; + int rval; + + if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY] == + SMIAPP_BINNING_CAPABILITY_NO) { + for (i = 0; i < ARRAY_SIZE(limits); i++) + sensor->limits[limits[i]] = + sensor->limits[limits_replace[i]]; + + return 0; + } + + rval = smiapp_get_limits(sensor, limits, ARRAY_SIZE(limits)); + if (rval < 0) + return rval; + + /* + * Sanity check whether the binning limits are valid. If not, + * use the non-binning ones. + */ + if (sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN] + && sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN] + && sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]) + return 0; + + for (i = 0; i < ARRAY_SIZE(limits); i++) { + dev_dbg(&client->dev, + "replace limit 0x%8.8x \"%s\" = %d, 0x%x\n", + smiapp_reg_limits[limits[i]].addr, + smiapp_reg_limits[limits[i]].what, + sensor->limits[limits_replace[i]], + sensor->limits[limits_replace[i]]); + sensor->limits[limits[i]] = + sensor->limits[limits_replace[i]]; + } + + return 0; +} + +static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int type, n; + unsigned int i, pixel_order; + int rval; + + rval = smiapp_read( + sensor, SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE, &type); + if (rval) + return rval; + + dev_dbg(&client->dev, "data_format_model_type %d\n", type); + + rval = smiapp_read(sensor, SMIAPP_REG_U8_PIXEL_ORDER, + &pixel_order); + if (rval) + return rval; + + if (pixel_order >= ARRAY_SIZE(pixel_order_str)) { + dev_dbg(&client->dev, "bad pixel order %d\n", pixel_order); + return -EINVAL; + } + + dev_dbg(&client->dev, "pixel order %d (%s)\n", pixel_order, + pixel_order_str[pixel_order]); + + switch (type) { + case SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL: + n = SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL_N; + break; + case SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED: + n = SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED_N; + break; + default: + return -EINVAL; + } + + sensor->default_pixel_order = pixel_order; + sensor->mbus_frame_fmts = 0; + + for (i = 0; i < n; i++) { + unsigned int fmt, j; + + rval = smiapp_read( + sensor, + SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(i), &fmt); + if (rval) + return rval; + + dev_dbg(&client->dev, "bpp %d, compressed %d\n", + fmt >> 8, (u8)fmt); + + for (j = 0; j < ARRAY_SIZE(smiapp_csi_data_formats); j++) { + const struct smiapp_csi_data_format *f = + &smiapp_csi_data_formats[j]; + + if (f->pixel_order != SMIAPP_PIXEL_ORDER_GRBG) + continue; + + if (f->width != fmt >> 8 || f->compressed != (u8)fmt) + continue; + + dev_dbg(&client->dev, "jolly good! %d\n", j); + + sensor->default_mbus_frame_fmts |= 1 << j; + if (!sensor->csi_format) { + sensor->csi_format = f; + sensor->internal_csi_format = f; + } + } + } + + if (!sensor->csi_format) { + dev_err(&client->dev, "no supported mbus code found\n"); + return -EINVAL; + } + + smiapp_update_mbus_formats(sensor); + + return 0; +} + +static void smiapp_update_blanking(struct smiapp_sensor *sensor) +{ + struct v4l2_ctrl *vblank = sensor->vblank; + struct v4l2_ctrl *hblank = sensor->hblank; + + vblank->minimum = + max_t(int, + sensor->limits[SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES], + sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN] - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height); + vblank->maximum = + sensor->limits[SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN] - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height; + + vblank->val = clamp_t(int, vblank->val, + vblank->minimum, vblank->maximum); + vblank->default_value = vblank->minimum; + vblank->val = vblank->val; + vblank->cur.val = vblank->val; + + hblank->minimum = + max_t(int, + sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN] - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width, + sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]); + hblank->maximum = + sensor->limits[SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN] - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width; + + hblank->val = clamp_t(int, hblank->val, + hblank->minimum, hblank->maximum); + hblank->default_value = hblank->minimum; + hblank->val = hblank->val; + hblank->cur.val = hblank->val; + + __smiapp_update_exposure_limits(sensor); +} + +static int smiapp_update_mode(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int binning_mode; + int rval; + + dev_dbg(&client->dev, "frame size: %dx%d\n", + sensor->src->crop[SMIAPP_PAD_SRC].width, + sensor->src->crop[SMIAPP_PAD_SRC].height); + dev_dbg(&client->dev, "csi format width: %d\n", + sensor->csi_format->width); + + /* Binning has to be set up here; it affects limits */ + if (sensor->binning_horizontal == 1 && + sensor->binning_vertical == 1) { + binning_mode = 0; + } else { + u8 binning_type = + (sensor->binning_horizontal << 4) + | sensor->binning_vertical; + + rval = smiapp_write( + sensor, SMIAPP_REG_U8_BINNING_TYPE, binning_type); + if (rval < 0) + return rval; + + binning_mode = 1; + } + rval = smiapp_write(sensor, SMIAPP_REG_U8_BINNING_MODE, binning_mode); + if (rval < 0) + return rval; + + /* Get updated limits due to binning */ + rval = smiapp_get_limits_binning(sensor); + if (rval < 0) + return rval; + + rval = smiapp_pll_update(sensor); + if (rval < 0) + return rval; + + /* Output from pixel array, including blanking */ + smiapp_update_blanking(sensor); + + dev_dbg(&client->dev, "vblank\t\t%d\n", sensor->vblank->val); + dev_dbg(&client->dev, "hblank\t\t%d\n", sensor->hblank->val); + + dev_dbg(&client->dev, "real timeperframe\t100/%d\n", + sensor->pll.vt_pix_clk_freq_hz / + ((sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width + + sensor->hblank->val) * + (sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height + + sensor->vblank->val) / 100)); + + return 0; +} + +/* + * + * SMIA++ NVM handling + * + */ +static int smiapp_read_nvm(struct smiapp_sensor *sensor, + unsigned char *nvm) +{ + u32 i, s, p, np, v; + int rval = 0, rval2; + + np = sensor->nvm_size / SMIAPP_NVM_PAGE_SIZE; + for (p = 0; p < np; p++) { + rval = smiapp_write( + sensor, + SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT, p); + if (rval) + goto out; + + rval = smiapp_write(sensor, + SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, + SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN | + SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN); + if (rval) + goto out; + + for (i = 0; i < 1000; i++) { + rval = smiapp_read( + sensor, + SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS, &s); + + if (rval) + goto out; + + if (s & SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY) + break; + + if (--i == 0) { + rval = -ETIMEDOUT; + goto out; + } + + } + + for (i = 0; i < SMIAPP_NVM_PAGE_SIZE; i++) { + rval = smiapp_read( + sensor, + SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 + i, + &v); + if (rval) + goto out; + + *nvm++ = v; + } + } + +out: + rval2 = smiapp_write(sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, 0); + if (rval < 0) + return rval; + else + return rval2; +} + +/* + * + * SMIA++ CCI address control + * + */ +static int smiapp_change_cci_addr(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + u32 val; + + client->addr = sensor->platform_data->i2c_addr_dfl; + + rval = smiapp_write(sensor, + SMIAPP_REG_U8_CCI_ADDRESS_CONTROL, + sensor->platform_data->i2c_addr_alt << 1); + if (rval) + return rval; + + client->addr = sensor->platform_data->i2c_addr_alt; + + /* verify addr change went ok */ + rval = smiapp_read(sensor, SMIAPP_REG_U8_CCI_ADDRESS_CONTROL, &val); + if (rval) + return rval; + + if (val != sensor->platform_data->i2c_addr_alt << 1) + return -ENODEV; + + return 0; +} + +/* + * + * SMIA++ Mode Control + * + */ +static int smiapp_setup_flash_strobe(struct smiapp_sensor *sensor) +{ + struct smiapp_flash_strobe_parms *strobe_setup; + unsigned int ext_freq = sensor->platform_data->ext_clk; + u32 tmp; + u32 strobe_adjustment; + u32 strobe_width_high_rs; + int rval; + + strobe_setup = sensor->platform_data->strobe_setup; + + /* + * How to calculate registers related to strobe length. Please + * do not change, or if you do at least know what you're + * doing. :-) + * + * Sakari Ailus 2010-10-25 + * + * flash_strobe_length [us] / 10^6 = (tFlash_strobe_width_ctrl + * / EXTCLK freq [Hz]) * flash_strobe_adjustment + * + * tFlash_strobe_width_ctrl E N, [1 - 0xffff] + * flash_strobe_adjustment E N, [1 - 0xff] + * + * The formula above is written as below to keep it on one + * line: + * + * l / 10^6 = w / e * a + * + * Let's mark w * a by x: + * + * x = w * a + * + * Thus, we get: + * + * x = l * e / 10^6 + * + * The strobe width must be at least as long as requested, + * thus rounding upwards is needed. + * + * x = (l * e + 10^6 - 1) / 10^6 + * ----------------------------- + * + * Maximum possible accuracy is wanted at all times. Thus keep + * a as small as possible. + * + * Calculate a, assuming maximum w, with rounding upwards: + * + * a = (x + (2^16 - 1) - 1) / (2^16 - 1) + * ------------------------------------- + * + * Thus, we also get w, with that a, with rounding upwards: + * + * w = (x + a - 1) / a + * ------------------- + * + * To get limits: + * + * x E [1, (2^16 - 1) * (2^8 - 1)] + * + * Substituting maximum x to the original formula (with rounding), + * the maximum l is thus + * + * (2^16 - 1) * (2^8 - 1) * 10^6 = l * e + 10^6 - 1 + * + * l = (10^6 * (2^16 - 1) * (2^8 - 1) - 10^6 + 1) / e + * -------------------------------------------------- + * + * flash_strobe_length must be clamped between 1 and + * (10^6 * (2^16 - 1) * (2^8 - 1) - 10^6 + 1) / EXTCLK freq. + * + * Then, + * + * flash_strobe_adjustment = ((flash_strobe_length * + * EXTCLK freq + 10^6 - 1) / 10^6 + (2^16 - 1) - 1) / (2^16 - 1) + * + * tFlash_strobe_width_ctrl = ((flash_strobe_length * + * EXTCLK freq + 10^6 - 1) / 10^6 + + * flash_strobe_adjustment - 1) / flash_strobe_adjustment + */ + tmp = div_u64(1000000ULL * ((1 << 16) - 1) * ((1 << 8) - 1) - + 1000000 + 1, ext_freq); + strobe_setup->strobe_width_high_us = + clamp_t(u32, strobe_setup->strobe_width_high_us, 1, tmp); + + tmp = div_u64(((u64)strobe_setup->strobe_width_high_us * (u64)ext_freq + + 1000000 - 1), 1000000ULL); + strobe_adjustment = (tmp + (1 << 16) - 1 - 1) / ((1 << 16) - 1); + strobe_width_high_rs = (tmp + strobe_adjustment - 1) / + strobe_adjustment; + + rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_MODE_RS, + strobe_setup->mode); + if (rval < 0) + goto out; + + rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT, + strobe_adjustment); + if (rval < 0) + goto out; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL, + strobe_width_high_rs); + if (rval < 0) + goto out; + + rval = smiapp_write(sensor, SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL, + strobe_setup->strobe_delay); + if (rval < 0) + goto out; + + rval = smiapp_write(sensor, SMIAPP_REG_U16_FLASH_STROBE_START_POINT, + strobe_setup->stobe_start_point); + if (rval < 0) + goto out; + + rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_TRIGGER_RS, + strobe_setup->trigger); + +out: + sensor->platform_data->strobe_setup->trigger = 0; + + return rval; +} + +/* ----------------------------------------------------------------------------- + * Power management + */ + +static int smiapp_power_on(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int sleep; + int rval; + + rval = regulator_enable(sensor->vana); + if (rval) { + dev_err(&client->dev, "failed to enable vana regulator\n"); + return rval; + } + usleep_range(1000, 1000); + + if (sensor->platform_data->set_xclk) + rval = sensor->platform_data->set_xclk( + &sensor->src->sd, sensor->platform_data->ext_clk); + else + rval = clk_enable(sensor->ext_clk); + if (rval < 0) { + dev_dbg(&client->dev, "failed to set xclk\n"); + goto out_xclk_fail; + } + usleep_range(1000, 1000); + + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_set_value(sensor->platform_data->xshutdown, 1); + + sleep = SMIAPP_RESET_DELAY(sensor->platform_data->ext_clk); + usleep_range(sleep, sleep); + + /* + * Failures to respond to the address change command have been noticed. + * Those failures seem to be caused by the sensor requiring a longer + * boot time than advertised. An additional 10ms delay seems to work + * around the issue, but the SMIA++ I2C write retry hack makes the delay + * unnecessary. The failures need to be investigated to find a proper + * fix, and a delay will likely need to be added here if the I2C write + * retry hack is reverted before the root cause of the boot time issue + * is found. + */ + + if (sensor->platform_data->i2c_addr_alt) { + rval = smiapp_change_cci_addr(sensor); + if (rval) { + dev_err(&client->dev, "cci address change error\n"); + goto out_cci_addr_fail; + } + } + + rval = smiapp_write(sensor, SMIAPP_REG_U8_SOFTWARE_RESET, + SMIAPP_SOFTWARE_RESET); + if (rval < 0) { + dev_err(&client->dev, "software reset failed\n"); + goto out_cci_addr_fail; + } + + if (sensor->platform_data->i2c_addr_alt) { + rval = smiapp_change_cci_addr(sensor); + if (rval) { + dev_err(&client->dev, "cci address change error\n"); + goto out_cci_addr_fail; + } + } + + rval = smiapp_write(sensor, SMIAPP_REG_U16_COMPRESSION_MODE, + SMIAPP_COMPRESSION_MODE_SIMPLE_PREDICTOR); + if (rval) { + dev_err(&client->dev, "compression mode set failed\n"); + goto out_cci_addr_fail; + } + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ, + sensor->platform_data->ext_clk / (1000000 / (1 << 8))); + if (rval) { + dev_err(&client->dev, "extclk frequency set failed\n"); + goto out_cci_addr_fail; + } + + rval = smiapp_write(sensor, SMIAPP_REG_U8_CSI_LANE_MODE, + sensor->platform_data->lanes - 1); + if (rval) { + dev_err(&client->dev, "csi lane mode set failed\n"); + goto out_cci_addr_fail; + } + + rval = smiapp_write(sensor, SMIAPP_REG_U8_FAST_STANDBY_CTRL, + SMIAPP_FAST_STANDBY_CTRL_IMMEDIATE); + if (rval) { + dev_err(&client->dev, "fast standby set failed\n"); + goto out_cci_addr_fail; + } + + rval = smiapp_write(sensor, SMIAPP_REG_U8_CSI_SIGNALLING_MODE, + sensor->platform_data->csi_signalling_mode); + if (rval) { + dev_err(&client->dev, "csi signalling mode set failed\n"); + goto out_cci_addr_fail; + } + + /* DPHY control done by sensor based on requested link rate */ + rval = smiapp_write(sensor, SMIAPP_REG_U8_DPHY_CTRL, + SMIAPP_DPHY_CTRL_UI); + if (rval < 0) + return rval; + + rval = smiapp_call_quirk(sensor, post_poweron); + if (rval) { + dev_err(&client->dev, "post_poweron quirks failed\n"); + goto out_cci_addr_fail; + } + + /* Are we still initialising...? If yes, return here. */ + if (!sensor->pixel_array) + return 0; + + rval = v4l2_ctrl_handler_setup( + &sensor->pixel_array->ctrl_handler); + if (rval) + goto out_cci_addr_fail; + + rval = v4l2_ctrl_handler_setup(&sensor->src->ctrl_handler); + if (rval) + goto out_cci_addr_fail; + + mutex_lock(&sensor->mutex); + rval = smiapp_update_mode(sensor); + mutex_unlock(&sensor->mutex); + if (rval < 0) + goto out_cci_addr_fail; + + return 0; + +out_cci_addr_fail: + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_set_value(sensor->platform_data->xshutdown, 0); + if (sensor->platform_data->set_xclk) + sensor->platform_data->set_xclk(&sensor->src->sd, 0); + else + clk_disable(sensor->ext_clk); + +out_xclk_fail: + regulator_disable(sensor->vana); + return rval; +} + +static void smiapp_power_off(struct smiapp_sensor *sensor) +{ + /* + * Currently power/clock to lens are enable/disabled separately + * but they are essentially the same signals. So if the sensor is + * powered off while the lens is powered on the sensor does not + * really see a power off and next time the cci address change + * will fail. So do a soft reset explicitly here. + */ + if (sensor->platform_data->i2c_addr_alt) + smiapp_write(sensor, + SMIAPP_REG_U8_SOFTWARE_RESET, + SMIAPP_SOFTWARE_RESET); + + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_set_value(sensor->platform_data->xshutdown, 0); + if (sensor->platform_data->set_xclk) + sensor->platform_data->set_xclk(&sensor->src->sd, 0); + else + clk_disable(sensor->ext_clk); + usleep_range(5000, 5000); + regulator_disable(sensor->vana); + sensor->streaming = 0; +} + +static int smiapp_set_power(struct v4l2_subdev *subdev, int on) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int ret = 0; + + mutex_lock(&sensor->power_mutex); + + /* + * If the power count is modified from 0 to != 0 or from != 0 + * to 0, update the power state. + */ + if (!sensor->power_count == !on) + goto out; + + if (on) { + /* Power on and perform initialisation. */ + ret = smiapp_power_on(sensor); + if (ret < 0) + goto out; + } else { + smiapp_power_off(sensor); + } + + /* Update the power count. */ + sensor->power_count += on ? 1 : -1; + WARN_ON(sensor->power_count < 0); + +out: + mutex_unlock(&sensor->power_mutex); + return ret; +} + +/* ----------------------------------------------------------------------------- + * Video stream management + */ + +static int smiapp_start_streaming(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + mutex_lock(&sensor->mutex); + + rval = smiapp_write(sensor, SMIAPP_REG_U16_CSI_DATA_FORMAT, + (sensor->csi_format->width << 8) | + sensor->csi_format->compressed); + if (rval) + goto out; + + rval = smiapp_pll_configure(sensor); + if (rval) + goto out; + + /* Analog crop start coordinates */ + rval = smiapp_write(sensor, SMIAPP_REG_U16_X_ADDR_START, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].left); + if (rval < 0) + goto out; + + rval = smiapp_write(sensor, SMIAPP_REG_U16_Y_ADDR_START, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].top); + if (rval < 0) + goto out; + + /* Analog crop end coordinates */ + rval = smiapp_write( + sensor, SMIAPP_REG_U16_X_ADDR_END, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].left + + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width - 1); + if (rval < 0) + goto out; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_Y_ADDR_END, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].top + + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height - 1); + if (rval < 0) + goto out; + + /* + * Output from pixel array, including blanking, is set using + * controls below. No need to set here. + */ + + /* Digital crop */ + if (sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] + == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) { + rval = smiapp_write( + sensor, SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET, + sensor->scaler->crop[SMIAPP_PAD_SINK].left); + if (rval < 0) + goto out; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET, + sensor->scaler->crop[SMIAPP_PAD_SINK].top); + if (rval < 0) + goto out; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH, + sensor->scaler->crop[SMIAPP_PAD_SINK].width); + if (rval < 0) + goto out; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT, + sensor->scaler->crop[SMIAPP_PAD_SINK].height); + if (rval < 0) + goto out; + } + + /* Scaling */ + if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + != SMIAPP_SCALING_CAPABILITY_NONE) { + rval = smiapp_write(sensor, SMIAPP_REG_U16_SCALING_MODE, + sensor->scaling_mode); + if (rval < 0) + goto out; + + rval = smiapp_write(sensor, SMIAPP_REG_U16_SCALE_M, + sensor->scale_m); + if (rval < 0) + goto out; + } + + /* Output size from sensor */ + rval = smiapp_write(sensor, SMIAPP_REG_U16_X_OUTPUT_SIZE, + sensor->src->crop[SMIAPP_PAD_SRC].width); + if (rval < 0) + goto out; + rval = smiapp_write(sensor, SMIAPP_REG_U16_Y_OUTPUT_SIZE, + sensor->src->crop[SMIAPP_PAD_SRC].height); + if (rval < 0) + goto out; + + if ((sensor->flash_capability & + (SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE | + SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE)) && + sensor->platform_data->strobe_setup != NULL && + sensor->platform_data->strobe_setup->trigger != 0) { + rval = smiapp_setup_flash_strobe(sensor); + if (rval) + goto out; + } + + rval = smiapp_call_quirk(sensor, pre_streamon); + if (rval) { + dev_err(&client->dev, "pre_streamon quirks failed\n"); + goto out; + } + + rval = smiapp_write(sensor, SMIAPP_REG_U8_MODE_SELECT, + SMIAPP_MODE_SELECT_STREAMING); + +out: + mutex_unlock(&sensor->mutex); + + return rval; +} + +static int smiapp_stop_streaming(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + mutex_lock(&sensor->mutex); + rval = smiapp_write(sensor, SMIAPP_REG_U8_MODE_SELECT, + SMIAPP_MODE_SELECT_SOFTWARE_STANDBY); + if (rval) + goto out; + + rval = smiapp_call_quirk(sensor, post_streamoff); + if (rval) + dev_err(&client->dev, "post_streamoff quirks failed\n"); + +out: + mutex_unlock(&sensor->mutex); + return rval; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev video operations + */ + +static int smiapp_set_stream(struct v4l2_subdev *subdev, int enable) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int rval; + + if (sensor->streaming == enable) + return 0; + + if (enable) { + sensor->streaming = 1; + rval = smiapp_start_streaming(sensor); + if (rval < 0) + sensor->streaming = 0; + } else { + rval = smiapp_stop_streaming(sensor); + sensor->streaming = 0; + } + + return rval; +} + +static int smiapp_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + unsigned int i; + int idx = -1; + int rval = -EINVAL; + + mutex_lock(&sensor->mutex); + + dev_err(&client->dev, "subdev %s, pad %d, index %d\n", + subdev->name, code->pad, code->index); + + if (subdev != &sensor->src->sd || code->pad != SMIAPP_PAD_SRC) { + if (code->index) + goto out; + + code->code = sensor->internal_csi_format->code; + rval = 0; + goto out; + } + + for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) { + if (sensor->mbus_frame_fmts & (1 << i)) + idx++; + + if (idx == code->index) { + code->code = smiapp_csi_data_formats[i].code; + dev_err(&client->dev, "found index %d, i %d, code %x\n", + code->index, i, code->code); + rval = 0; + break; + } + } + +out: + mutex_unlock(&sensor->mutex); + + return rval; +} + +static u32 __smiapp_get_mbus_code(struct v4l2_subdev *subdev, + unsigned int pad) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + + if (subdev == &sensor->src->sd && pad == SMIAPP_PAD_SRC) + return sensor->csi_format->code; + else + return sensor->internal_csi_format->code; +} + +static int __smiapp_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + fmt->format = *v4l2_subdev_get_try_format(fh, fmt->pad); + } else { + struct v4l2_rect *r; + + if (fmt->pad == ssd->source_pad) + r = &ssd->crop[ssd->source_pad]; + else + r = &ssd->sink_fmt; + + fmt->format.code = __smiapp_get_mbus_code(subdev, fmt->pad); + fmt->format.width = r->width; + fmt->format.height = r->height; + } + + return 0; +} + +static int smiapp_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int rval; + + mutex_lock(&sensor->mutex); + rval = __smiapp_get_format(subdev, fh, fmt); + mutex_unlock(&sensor->mutex); + + return rval; +} + +static void smiapp_get_crop_compose(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_rect **crops, + struct v4l2_rect **comps, int which) +{ + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + unsigned int i; + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (crops) + for (i = 0; i < subdev->entity.num_pads; i++) + crops[i] = &ssd->crop[i]; + if (comps) + *comps = &ssd->compose; + } else { + if (crops) { + for (i = 0; i < subdev->entity.num_pads; i++) { + crops[i] = v4l2_subdev_get_try_crop(fh, i); + BUG_ON(!crops[i]); + } + } + if (comps) { + *comps = v4l2_subdev_get_try_compose(fh, + SMIAPP_PAD_SINK); + BUG_ON(!*comps); + } + } +} + +/* Changes require propagation only on sink pad. */ +static void smiapp_propagate(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, int which, + int target) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + struct v4l2_rect *comp, *crops[SMIAPP_PADS]; + + smiapp_get_crop_compose(subdev, fh, crops, &comp, which); + + switch (target) { + case V4L2_SEL_TGT_CROP: + comp->width = crops[SMIAPP_PAD_SINK]->width; + comp->height = crops[SMIAPP_PAD_SINK]->height; + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (ssd == sensor->scaler) { + sensor->scale_m = + sensor->limits[ + SMIAPP_LIMIT_SCALER_N_MIN]; + sensor->scaling_mode = + SMIAPP_SCALING_MODE_NONE; + } else if (ssd == sensor->binner) { + sensor->binning_horizontal = 1; + sensor->binning_vertical = 1; + } + } + /* Fall through */ + case V4L2_SEL_TGT_COMPOSE: + *crops[SMIAPP_PAD_SRC] = *comp; + break; + default: + BUG(); + } +} + +static const struct smiapp_csi_data_format +*smiapp_validate_csi_data_format(struct smiapp_sensor *sensor, u32 code) +{ + const struct smiapp_csi_data_format *csi_format = sensor->csi_format; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) { + if (sensor->mbus_frame_fmts & (1 << i) + && smiapp_csi_data_formats[i].code == code) + return &smiapp_csi_data_formats[i]; + } + + return csi_format; +} + +static int smiapp_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + struct v4l2_rect *crops[SMIAPP_PADS]; + + mutex_lock(&sensor->mutex); + + /* + * Media bus code is changeable on src subdev's source pad. On + * other source pads we just get format here. + */ + if (fmt->pad == ssd->source_pad) { + u32 code = fmt->format.code; + int rval = __smiapp_get_format(subdev, fh, fmt); + + if (!rval && subdev == &sensor->src->sd) { + const struct smiapp_csi_data_format *csi_format = + smiapp_validate_csi_data_format(sensor, code); + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + sensor->csi_format = csi_format; + fmt->format.code = csi_format->code; + } + + mutex_unlock(&sensor->mutex); + return rval; + } + + /* Sink pad. Width and height are changeable here. */ + fmt->format.code = __smiapp_get_mbus_code(subdev, fmt->pad); + fmt->format.width &= ~1; + fmt->format.height &= ~1; + + fmt->format.width = + clamp(fmt->format.width, + sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE], + sensor->limits[SMIAPP_LIMIT_MAX_X_OUTPUT_SIZE]); + fmt->format.height = + clamp(fmt->format.height, + sensor->limits[SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE], + sensor->limits[SMIAPP_LIMIT_MAX_Y_OUTPUT_SIZE]); + + smiapp_get_crop_compose(subdev, fh, crops, NULL, fmt->which); + + crops[ssd->sink_pad]->left = 0; + crops[ssd->sink_pad]->top = 0; + crops[ssd->sink_pad]->width = fmt->format.width; + crops[ssd->sink_pad]->height = fmt->format.height; + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + ssd->sink_fmt = *crops[ssd->sink_pad]; + smiapp_propagate(subdev, fh, fmt->which, + V4L2_SEL_TGT_CROP); + + mutex_unlock(&sensor->mutex); + + return 0; +} + +/* + * Calculate goodness of scaled image size compared to expected image + * size and flags provided. + */ +#define SCALING_GOODNESS 100000 +#define SCALING_GOODNESS_EXTREME 100000000 +static int scaling_goodness(struct v4l2_subdev *subdev, int w, int ask_w, + int h, int ask_h, u32 flags) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct i2c_client *client = v4l2_get_subdevdata(subdev); + int val = 0; + + w &= ~1; + ask_w &= ~1; + h &= ~1; + ask_h &= ~1; + + if (flags & V4L2_SEL_FLAG_GE) { + if (w < ask_w) + val -= SCALING_GOODNESS; + if (h < ask_h) + val -= SCALING_GOODNESS; + } + + if (flags & V4L2_SEL_FLAG_LE) { + if (w > ask_w) + val -= SCALING_GOODNESS; + if (h > ask_h) + val -= SCALING_GOODNESS; + } + + val -= abs(w - ask_w); + val -= abs(h - ask_h); + + if (w < sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE]) + val -= SCALING_GOODNESS_EXTREME; + + dev_dbg(&client->dev, "w %d ask_w %d h %d ask_h %d goodness %d\n", + w, ask_h, h, ask_h, val); + + return val; +} + +static void smiapp_set_compose_binner(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel, + struct v4l2_rect **crops, + struct v4l2_rect *comp) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + unsigned int i; + unsigned int binh = 1, binv = 1; + unsigned int best = scaling_goodness( + subdev, + crops[SMIAPP_PAD_SINK]->width, sel->r.width, + crops[SMIAPP_PAD_SINK]->height, sel->r.height, sel->flags); + + for (i = 0; i < sensor->nbinning_subtypes; i++) { + int this = scaling_goodness( + subdev, + crops[SMIAPP_PAD_SINK]->width + / sensor->binning_subtypes[i].horizontal, + sel->r.width, + crops[SMIAPP_PAD_SINK]->height + / sensor->binning_subtypes[i].vertical, + sel->r.height, sel->flags); + + if (this > best) { + binh = sensor->binning_subtypes[i].horizontal; + binv = sensor->binning_subtypes[i].vertical; + best = this; + } + } + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + sensor->binning_vertical = binv; + sensor->binning_horizontal = binh; + } + + sel->r.width = (crops[SMIAPP_PAD_SINK]->width / binh) & ~1; + sel->r.height = (crops[SMIAPP_PAD_SINK]->height / binv) & ~1; +} + +/* + * Calculate best scaling ratio and mode for given output resolution. + * + * Try all of these: horizontal ratio, vertical ratio and smallest + * size possible (horizontally). + * + * Also try whether horizontal scaler or full scaler gives a better + * result. + */ +static void smiapp_set_compose_scaler(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel, + struct v4l2_rect **crops, + struct v4l2_rect *comp) +{ + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + u32 min, max, a, b, max_m; + u32 scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; + int mode = SMIAPP_SCALING_MODE_HORIZONTAL; + u32 try[4]; + u32 ntry = 0; + unsigned int i; + int best = INT_MIN; + + sel->r.width = min_t(unsigned int, sel->r.width, + crops[SMIAPP_PAD_SINK]->width); + sel->r.height = min_t(unsigned int, sel->r.height, + crops[SMIAPP_PAD_SINK]->height); + + a = crops[SMIAPP_PAD_SINK]->width + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] / sel->r.width; + b = crops[SMIAPP_PAD_SINK]->height + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] / sel->r.height; + max_m = crops[SMIAPP_PAD_SINK]->width + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] + / sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE]; + + a = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX], + max(a, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN])); + b = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX], + max(b, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN])); + max_m = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX], + max(max_m, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN])); + + dev_dbg(&client->dev, "scaling: a %d b %d max_m %d\n", a, b, max_m); + + min = min(max_m, min(a, b)); + max = min(max_m, max(a, b)); + + try[ntry] = min; + ntry++; + if (min != max) { + try[ntry] = max; + ntry++; + } + if (max != max_m) { + try[ntry] = min + 1; + ntry++; + if (min != max) { + try[ntry] = max + 1; + ntry++; + } + } + + for (i = 0; i < ntry; i++) { + int this = scaling_goodness( + subdev, + crops[SMIAPP_PAD_SINK]->width + / try[i] + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN], + sel->r.width, + crops[SMIAPP_PAD_SINK]->height, + sel->r.height, + sel->flags); + + dev_dbg(&client->dev, "trying factor %d (%d)\n", try[i], i); + + if (this > best) { + scale_m = try[i]; + mode = SMIAPP_SCALING_MODE_HORIZONTAL; + best = this; + } + + if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + == SMIAPP_SCALING_CAPABILITY_HORIZONTAL) + continue; + + this = scaling_goodness( + subdev, crops[SMIAPP_PAD_SINK]->width + / try[i] + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN], + sel->r.width, + crops[SMIAPP_PAD_SINK]->height + / try[i] + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN], + sel->r.height, + sel->flags); + + if (this > best) { + scale_m = try[i]; + mode = SMIAPP_SCALING_MODE_BOTH; + best = this; + } + } + + sel->r.width = + (crops[SMIAPP_PAD_SINK]->width + / scale_m + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]) & ~1; + if (mode == SMIAPP_SCALING_MODE_BOTH) + sel->r.height = + (crops[SMIAPP_PAD_SINK]->height + / scale_m + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]) + & ~1; + else + sel->r.height = crops[SMIAPP_PAD_SINK]->height; + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + sensor->scale_m = scale_m; + sensor->scaling_mode = mode; + } +} +/* We're only called on source pads. This function sets scaling. */ +static int smiapp_set_compose(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + struct v4l2_rect *comp, *crops[SMIAPP_PADS]; + + smiapp_get_crop_compose(subdev, fh, crops, &comp, sel->which); + + sel->r.top = 0; + sel->r.left = 0; + + if (ssd == sensor->binner) + smiapp_set_compose_binner(subdev, fh, sel, crops, comp); + else + smiapp_set_compose_scaler(subdev, fh, sel, crops, comp); + + *comp = sel->r; + smiapp_propagate(subdev, fh, sel->which, + V4L2_SEL_TGT_COMPOSE); + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return smiapp_update_mode(sensor); + + return 0; +} + +static int __smiapp_sel_supported(struct v4l2_subdev *subdev, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + + /* We only implement crop in three places. */ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP_BOUNDS: + if (ssd == sensor->pixel_array + && sel->pad == SMIAPP_PA_PAD_SRC) + return 0; + if (ssd == sensor->src + && sel->pad == SMIAPP_PAD_SRC) + return 0; + if (ssd == sensor->scaler + && sel->pad == SMIAPP_PAD_SINK + && sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] + == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) + return 0; + return -EINVAL; + case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + if (sel->pad == ssd->source_pad) + return -EINVAL; + if (ssd == sensor->binner) + return 0; + if (ssd == sensor->scaler + && sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + != SMIAPP_SCALING_CAPABILITY_NONE) + return 0; + /* Fall through */ + default: + return -EINVAL; + } +} + +static int smiapp_set_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + struct v4l2_rect *src_size, *crops[SMIAPP_PADS]; + struct v4l2_rect _r; + + smiapp_get_crop_compose(subdev, fh, crops, NULL, sel->which); + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (sel->pad == ssd->sink_pad) + src_size = &ssd->sink_fmt; + else + src_size = &ssd->compose; + } else { + if (sel->pad == ssd->sink_pad) { + _r.left = 0; + _r.top = 0; + _r.width = v4l2_subdev_get_try_format(fh, sel->pad) + ->width; + _r.height = v4l2_subdev_get_try_format(fh, sel->pad) + ->height; + src_size = &_r; + } else { + src_size = + v4l2_subdev_get_try_compose( + fh, ssd->sink_pad); + } + } + + if (ssd == sensor->src && sel->pad == SMIAPP_PAD_SRC) { + sel->r.left = 0; + sel->r.top = 0; + } + + sel->r.width = min(sel->r.width, src_size->width); + sel->r.height = min(sel->r.height, src_size->height); + + sel->r.left = min(sel->r.left, src_size->width - sel->r.width); + sel->r.top = min(sel->r.top, src_size->height - sel->r.height); + + *crops[sel->pad] = sel->r; + + if (ssd != sensor->pixel_array && sel->pad == SMIAPP_PAD_SINK) + smiapp_propagate(subdev, fh, sel->which, + V4L2_SEL_TGT_CROP); + + return 0; +} + +static int __smiapp_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + struct v4l2_rect *comp, *crops[SMIAPP_PADS]; + struct v4l2_rect sink_fmt; + int ret; + + ret = __smiapp_sel_supported(subdev, sel); + if (ret) + return ret; + + smiapp_get_crop_compose(subdev, fh, crops, &comp, sel->which); + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + sink_fmt = ssd->sink_fmt; + } else { + struct v4l2_mbus_framefmt *fmt = + v4l2_subdev_get_try_format(fh, ssd->sink_pad); + + sink_fmt.left = 0; + sink_fmt.top = 0; + sink_fmt.width = fmt->width; + sink_fmt.height = fmt->height; + } + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + if (ssd == sensor->pixel_array) { + sel->r.width = + sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; + sel->r.height = + sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1; + } else if (sel->pad == ssd->sink_pad) { + sel->r = sink_fmt; + } else { + sel->r = *comp; + } + break; + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + sel->r = *crops[sel->pad]; + break; + case V4L2_SEL_TGT_COMPOSE: + sel->r = *comp; + break; + } + + return 0; +} + +static int smiapp_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int rval; + + mutex_lock(&sensor->mutex); + rval = __smiapp_get_selection(subdev, fh, sel); + mutex_unlock(&sensor->mutex); + + return rval; +} +static int smiapp_set_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int ret; + + ret = __smiapp_sel_supported(subdev, sel); + if (ret) + return ret; + + mutex_lock(&sensor->mutex); + + sel->r.left = max(0, sel->r.left & ~1); + sel->r.top = max(0, sel->r.top & ~1); + sel->r.width = max(0, SMIAPP_ALIGN_DIM(sel->r.width, sel->flags)); + sel->r.height = max(0, SMIAPP_ALIGN_DIM(sel->r.height, sel->flags)); + + sel->r.width = max_t(unsigned int, + sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE], + sel->r.width); + sel->r.height = max_t(unsigned int, + sensor->limits[SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE], + sel->r.height); + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + ret = smiapp_set_crop(subdev, fh, sel); + break; + case V4L2_SEL_TGT_COMPOSE: + ret = smiapp_set_compose(subdev, fh, sel); + break; + default: + BUG(); + } + + mutex_unlock(&sensor->mutex); + return ret; +} + +static int smiapp_get_skip_frames(struct v4l2_subdev *subdev, u32 *frames) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + + *frames = sensor->frame_skip; + return 0; +} + +/* ----------------------------------------------------------------------------- + * sysfs attributes + */ + +static ssize_t +smiapp_sysfs_nvm_read(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev)); + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + unsigned int nbytes; + + if (!sensor->dev_init_done) + return -EBUSY; + + if (!sensor->nvm_size) { + /* NVM not read yet - read it now */ + sensor->nvm_size = sensor->platform_data->nvm_size; + if (smiapp_set_power(subdev, 1) < 0) + return -ENODEV; + if (smiapp_read_nvm(sensor, sensor->nvm)) { + dev_err(&client->dev, "nvm read failed\n"); + return -ENODEV; + } + smiapp_set_power(subdev, 0); + } + /* + * NVM is still way below a PAGE_SIZE, so we can safely + * assume this for now. + */ + nbytes = min_t(unsigned int, sensor->nvm_size, PAGE_SIZE); + memcpy(buf, sensor->nvm, nbytes); + + return nbytes; +} +static DEVICE_ATTR(nvm, S_IRUGO, smiapp_sysfs_nvm_read, NULL); + +/* ----------------------------------------------------------------------------- + * V4L2 subdev core operations + */ + +static int smiapp_identify_module(struct v4l2_subdev *subdev) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct smiapp_module_info *minfo = &sensor->minfo; + unsigned int i; + int rval = 0; + + minfo->name = SMIAPP_NAME; + + /* Module info */ + rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_MANUFACTURER_ID, + &minfo->manufacturer_id); + if (!rval) + rval = smiapp_read_8only(sensor, SMIAPP_REG_U16_MODEL_ID, + &minfo->model_id); + if (!rval) + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_REVISION_NUMBER_MAJOR, + &minfo->revision_number_major); + if (!rval) + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_REVISION_NUMBER_MINOR, + &minfo->revision_number_minor); + if (!rval) + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_MODULE_DATE_YEAR, + &minfo->module_year); + if (!rval) + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_MODULE_DATE_MONTH, + &minfo->module_month); + if (!rval) + rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_MODULE_DATE_DAY, + &minfo->module_day); + + /* Sensor info */ + if (!rval) + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID, + &minfo->sensor_manufacturer_id); + if (!rval) + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U16_SENSOR_MODEL_ID, + &minfo->sensor_model_id); + if (!rval) + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_SENSOR_REVISION_NUMBER, + &minfo->sensor_revision_number); + if (!rval) + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION, + &minfo->sensor_firmware_version); + + /* SMIA */ + if (!rval) + rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_SMIA_VERSION, + &minfo->smia_version); + if (!rval) + rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_SMIAPP_VERSION, + &minfo->smiapp_version); + + if (rval) { + dev_err(&client->dev, "sensor detection failed\n"); + return -ENODEV; + } + + dev_dbg(&client->dev, "module 0x%2.2x-0x%4.4x\n", + minfo->manufacturer_id, minfo->model_id); + + dev_dbg(&client->dev, + "module revision 0x%2.2x-0x%2.2x date %2.2d-%2.2d-%2.2d\n", + minfo->revision_number_major, minfo->revision_number_minor, + minfo->module_year, minfo->module_month, minfo->module_day); + + dev_dbg(&client->dev, "sensor 0x%2.2x-0x%4.4x\n", + minfo->sensor_manufacturer_id, minfo->sensor_model_id); + + dev_dbg(&client->dev, + "sensor revision 0x%2.2x firmware version 0x%2.2x\n", + minfo->sensor_revision_number, minfo->sensor_firmware_version); + + dev_dbg(&client->dev, "smia version %2.2d smiapp version %2.2d\n", + minfo->smia_version, minfo->smiapp_version); + + /* + * Some modules have bad data in the lvalues below. Hope the + * rvalues have better stuff. The lvalues are module + * parameters whereas the rvalues are sensor parameters. + */ + if (!minfo->manufacturer_id && !minfo->model_id) { + minfo->manufacturer_id = minfo->sensor_manufacturer_id; + minfo->model_id = minfo->sensor_model_id; + minfo->revision_number_major = minfo->sensor_revision_number; + } + + for (i = 0; i < ARRAY_SIZE(smiapp_module_idents); i++) { + if (smiapp_module_idents[i].manufacturer_id + != minfo->manufacturer_id) + continue; + if (smiapp_module_idents[i].model_id != minfo->model_id) + continue; + if (smiapp_module_idents[i].flags + & SMIAPP_MODULE_IDENT_FLAG_REV_LE) { + if (smiapp_module_idents[i].revision_number_major + < minfo->revision_number_major) + continue; + } else { + if (smiapp_module_idents[i].revision_number_major + != minfo->revision_number_major) + continue; + } + + minfo->name = smiapp_module_idents[i].name; + minfo->quirk = smiapp_module_idents[i].quirk; + break; + } + + if (i >= ARRAY_SIZE(smiapp_module_idents)) + dev_warn(&client->dev, + "no quirks for this module; let's hope it's fully compliant\n"); + + dev_dbg(&client->dev, "the sensor is called %s, ident %2.2x%4.4x%2.2x\n", + minfo->name, minfo->manufacturer_id, minfo->model_id, + minfo->revision_number_major); + + strlcpy(subdev->name, sensor->minfo.name, sizeof(subdev->name)); + + return 0; +} + +static const struct v4l2_subdev_ops smiapp_ops; +static const struct v4l2_subdev_internal_ops smiapp_internal_ops; +static const struct media_entity_operations smiapp_entity_ops; + +static int smiapp_registered(struct v4l2_subdev *subdev) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct smiapp_subdev *last = NULL; + u32 tmp; + unsigned int i; + int rval; + + sensor->vana = regulator_get(&client->dev, "VANA"); + if (IS_ERR(sensor->vana)) { + dev_err(&client->dev, "could not get regulator for vana\n"); + return -ENODEV; + } + + if (!sensor->platform_data->set_xclk) { + sensor->ext_clk = clk_get(&client->dev, + sensor->platform_data->ext_clk_name); + if (IS_ERR(sensor->ext_clk)) { + dev_err(&client->dev, "could not get clock %s\n", + sensor->platform_data->ext_clk_name); + rval = -ENODEV; + goto out_clk_get; + } + + rval = clk_set_rate(sensor->ext_clk, + sensor->platform_data->ext_clk); + if (rval < 0) { + dev_err(&client->dev, + "unable to set clock %s freq to %u\n", + sensor->platform_data->ext_clk_name, + sensor->platform_data->ext_clk); + rval = -ENODEV; + goto out_clk_set_rate; + } + } + + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) { + if (gpio_request_one(sensor->platform_data->xshutdown, 0, + "SMIA++ xshutdown") != 0) { + dev_err(&client->dev, + "unable to acquire reset gpio %d\n", + sensor->platform_data->xshutdown); + rval = -ENODEV; + goto out_clk_set_rate; + } + } + + rval = smiapp_power_on(sensor); + if (rval) { + rval = -ENODEV; + goto out_smiapp_power_on; + } + + rval = smiapp_identify_module(subdev); + if (rval) { + rval = -ENODEV; + goto out_power_off; + } + + rval = smiapp_get_all_limits(sensor); + if (rval) { + rval = -ENODEV; + goto out_power_off; + } + + /* + * Handle Sensor Module orientation on the board. + * + * The application of H-FLIP and V-FLIP on the sensor is modified by + * the sensor orientation on the board. + * + * For SMIAPP_BOARD_SENSOR_ORIENT_180 the default behaviour is to set + * both H-FLIP and V-FLIP for normal operation which also implies + * that a set/unset operation for user space HFLIP and VFLIP v4l2 + * controls will need to be internally inverted. + * + * Rotation also changes the bayer pattern. + */ + if (sensor->platform_data->module_board_orient == + SMIAPP_MODULE_BOARD_ORIENT_180) + sensor->hvflip_inv_mask = SMIAPP_IMAGE_ORIENTATION_HFLIP | + SMIAPP_IMAGE_ORIENTATION_VFLIP; + + rval = smiapp_get_mbus_formats(sensor); + if (rval) { + rval = -ENODEV; + goto out_power_off; + } + + if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY]) { + u32 val; + + rval = smiapp_read(sensor, + SMIAPP_REG_U8_BINNING_SUBTYPES, &val); + if (rval < 0) { + rval = -ENODEV; + goto out_power_off; + } + sensor->nbinning_subtypes = min_t(u8, val, + SMIAPP_BINNING_SUBTYPES); + + for (i = 0; i < sensor->nbinning_subtypes; i++) { + rval = smiapp_read( + sensor, SMIAPP_REG_U8_BINNING_TYPE_n(i), &val); + if (rval < 0) { + rval = -ENODEV; + goto out_power_off; + } + sensor->binning_subtypes[i] = + *(struct smiapp_binning_subtype *)&val; + + dev_dbg(&client->dev, "binning %xx%x\n", + sensor->binning_subtypes[i].horizontal, + sensor->binning_subtypes[i].vertical); + } + } + sensor->binning_horizontal = 1; + sensor->binning_vertical = 1; + + /* SMIA++ NVM initialization - it will be read from the sensor + * when it is first requested by userspace. + */ + if (sensor->minfo.smiapp_version && sensor->platform_data->nvm_size) { + sensor->nvm = kzalloc(sensor->platform_data->nvm_size, + GFP_KERNEL); + if (sensor->nvm == NULL) { + dev_err(&client->dev, "nvm buf allocation failed\n"); + rval = -ENOMEM; + goto out_power_off; + } + + if (device_create_file(&client->dev, &dev_attr_nvm) != 0) { + dev_err(&client->dev, "sysfs nvm entry failed\n"); + rval = -EBUSY; + goto out_power_off; + } + } + + rval = smiapp_call_quirk(sensor, limits); + if (rval) { + dev_err(&client->dev, "limits quirks failed\n"); + goto out_nvm_release; + } + + /* We consider this as profile 0 sensor if any of these are zero. */ + if (!sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV] || + !sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV] || + !sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV] || + !sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV]) { + sensor->minfo.smiapp_profile = SMIAPP_PROFILE_0; + } else if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + != SMIAPP_SCALING_CAPABILITY_NONE) { + if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + == SMIAPP_SCALING_CAPABILITY_HORIZONTAL) + sensor->minfo.smiapp_profile = SMIAPP_PROFILE_1; + else + sensor->minfo.smiapp_profile = SMIAPP_PROFILE_2; + sensor->scaler = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + } else if (sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] + == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) { + sensor->scaler = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + } + sensor->binner = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + sensor->pixel_array = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + + sensor->scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; + + for (i = 0; i < SMIAPP_SUBDEVS; i++) { + struct { + struct smiapp_subdev *ssd; + char *name; + } const __this[] = { + { sensor->scaler, "scaler", }, + { sensor->binner, "binner", }, + { sensor->pixel_array, "pixel array", }, + }, *_this = &__this[i]; + struct smiapp_subdev *this = _this->ssd; + + if (!this) + continue; + + if (this != sensor->src) + v4l2_subdev_init(&this->sd, &smiapp_ops); + + this->sensor = sensor; + + if (this == sensor->pixel_array) { + this->npads = 1; + } else { + this->npads = 2; + this->source_pad = 1; + } + + snprintf(this->sd.name, + sizeof(this->sd.name), "%s %s", + sensor->minfo.name, _this->name); + + this->sink_fmt.width = + sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; + this->sink_fmt.height = + sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1; + this->compose.width = this->sink_fmt.width; + this->compose.height = this->sink_fmt.height; + this->crop[this->source_pad] = this->compose; + this->pads[this->source_pad].flags = MEDIA_PAD_FL_SOURCE; + if (this != sensor->pixel_array) { + this->crop[this->sink_pad] = this->compose; + this->pads[this->sink_pad].flags = MEDIA_PAD_FL_SINK; + } + + this->sd.entity.ops = &smiapp_entity_ops; + + if (last == NULL) { + last = this; + continue; + } + + this->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + this->sd.internal_ops = &smiapp_internal_ops; + this->sd.owner = NULL; + v4l2_set_subdevdata(&this->sd, client); + + rval = media_entity_init(&this->sd.entity, + this->npads, this->pads, 0); + if (rval) { + dev_err(&client->dev, + "media_entity_init failed\n"); + goto out_nvm_release; + } + + rval = media_entity_create_link(&this->sd.entity, + this->source_pad, + &last->sd.entity, + last->sink_pad, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (rval) { + dev_err(&client->dev, + "media_entity_create_link failed\n"); + goto out_nvm_release; + } + + rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev, + &this->sd); + if (rval) { + dev_err(&client->dev, + "v4l2_device_register_subdev failed\n"); + goto out_nvm_release; + } + + last = this; + } + + dev_dbg(&client->dev, "profile %d\n", sensor->minfo.smiapp_profile); + + sensor->pixel_array->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; + + /* final steps */ + smiapp_read_frame_fmt(sensor); + rval = smiapp_init_controls(sensor); + if (rval < 0) + goto out_nvm_release; + + rval = smiapp_update_mode(sensor); + if (rval) { + dev_err(&client->dev, "update mode failed\n"); + goto out_nvm_release; + } + + sensor->streaming = false; + sensor->dev_init_done = true; + + /* check flash capability */ + rval = smiapp_read(sensor, SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, &tmp); + sensor->flash_capability = tmp; + if (rval) + goto out_nvm_release; + + smiapp_power_off(sensor); + + return 0; + +out_nvm_release: + device_remove_file(&client->dev, &dev_attr_nvm); + +out_power_off: + kfree(sensor->nvm); + sensor->nvm = NULL; + smiapp_power_off(sensor); + +out_smiapp_power_on: + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_free(sensor->platform_data->xshutdown); + +out_clk_set_rate: + clk_put(sensor->ext_clk); + sensor->ext_clk = NULL; + +out_clk_get: + regulator_put(sensor->vana); + sensor->vana = NULL; + return rval; +} + +static int smiapp_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct smiapp_subdev *ssd = to_smiapp_subdev(sd); + struct smiapp_sensor *sensor = ssd->sensor; + u32 mbus_code = + smiapp_csi_data_formats[smiapp_pixel_order(sensor)].code; + unsigned int i; + + mutex_lock(&sensor->mutex); + + for (i = 0; i < ssd->npads; i++) { + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(fh, i); + struct v4l2_rect *try_crop = v4l2_subdev_get_try_crop(fh, i); + struct v4l2_rect *try_comp; + + try_fmt->width = sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; + try_fmt->height = sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1; + try_fmt->code = mbus_code; + + try_crop->top = 0; + try_crop->left = 0; + try_crop->width = try_fmt->width; + try_crop->height = try_fmt->height; + + if (ssd != sensor->pixel_array) + continue; + + try_comp = v4l2_subdev_get_try_compose(fh, i); + *try_comp = *try_crop; + } + + mutex_unlock(&sensor->mutex); + + return smiapp_set_power(sd, 1); +} + +static int smiapp_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + return smiapp_set_power(sd, 0); +} + +static const struct v4l2_subdev_video_ops smiapp_video_ops = { + .s_stream = smiapp_set_stream, +}; + +static const struct v4l2_subdev_core_ops smiapp_core_ops = { + .s_power = smiapp_set_power, +}; + +static const struct v4l2_subdev_pad_ops smiapp_pad_ops = { + .enum_mbus_code = smiapp_enum_mbus_code, + .get_fmt = smiapp_get_format, + .set_fmt = smiapp_set_format, + .get_selection = smiapp_get_selection, + .set_selection = smiapp_set_selection, +}; + +static const struct v4l2_subdev_sensor_ops smiapp_sensor_ops = { + .g_skip_frames = smiapp_get_skip_frames, +}; + +static const struct v4l2_subdev_ops smiapp_ops = { + .core = &smiapp_core_ops, + .video = &smiapp_video_ops, + .pad = &smiapp_pad_ops, + .sensor = &smiapp_sensor_ops, +}; + +static const struct media_entity_operations smiapp_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops smiapp_internal_src_ops = { + .registered = smiapp_registered, + .open = smiapp_open, + .close = smiapp_close, +}; + +static const struct v4l2_subdev_internal_ops smiapp_internal_ops = { + .open = smiapp_open, + .close = smiapp_close, +}; + +/* ----------------------------------------------------------------------------- + * I2C Driver + */ + +#ifdef CONFIG_PM + +static int smiapp_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + bool streaming; + + BUG_ON(mutex_is_locked(&sensor->mutex)); + + if (sensor->power_count == 0) + return 0; + + if (sensor->streaming) + smiapp_stop_streaming(sensor); + + streaming = sensor->streaming; + + smiapp_power_off(sensor); + + /* save state for resume */ + sensor->streaming = streaming; + + return 0; +} + +static int smiapp_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int rval; + + if (sensor->power_count == 0) + return 0; + + rval = smiapp_power_on(sensor); + if (rval) + return rval; + + if (sensor->streaming) + rval = smiapp_start_streaming(sensor); + + return rval; +} + +#else + +#define smiapp_suspend NULL +#define smiapp_resume NULL + +#endif /* CONFIG_PM */ + +static int smiapp_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + struct smiapp_sensor *sensor; + int rval; + + if (client->dev.platform_data == NULL) + return -ENODEV; + + sensor = kzalloc(sizeof(*sensor), GFP_KERNEL); + if (sensor == NULL) + return -ENOMEM; + + sensor->platform_data = client->dev.platform_data; + mutex_init(&sensor->mutex); + mutex_init(&sensor->power_mutex); + sensor->src = &sensor->ssds[sensor->ssds_used]; + + v4l2_i2c_subdev_init(&sensor->src->sd, client, &smiapp_ops); + sensor->src->sd.internal_ops = &smiapp_internal_src_ops; + sensor->src->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sensor->src->sensor = sensor; + + sensor->src->pads[0].flags = MEDIA_PAD_FL_SOURCE; + rval = media_entity_init(&sensor->src->sd.entity, 2, + sensor->src->pads, 0); + if (rval < 0) + kfree(sensor); + + return rval; +} + +static int __exit smiapp_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + unsigned int i; + + if (sensor->power_count) { + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_set_value(sensor->platform_data->xshutdown, 0); + if (sensor->platform_data->set_xclk) + sensor->platform_data->set_xclk(&sensor->src->sd, 0); + else + clk_disable(sensor->ext_clk); + sensor->power_count = 0; + } + + if (sensor->nvm) { + device_remove_file(&client->dev, &dev_attr_nvm); + kfree(sensor->nvm); + } + + for (i = 0; i < sensor->ssds_used; i++) { + media_entity_cleanup(&sensor->ssds[i].sd.entity); + v4l2_device_unregister_subdev(&sensor->ssds[i].sd); + } + smiapp_free_controls(sensor); + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_free(sensor->platform_data->xshutdown); + if (sensor->ext_clk) + clk_put(sensor->ext_clk); + if (sensor->vana) + regulator_put(sensor->vana); + + kfree(sensor); + + return 0; +} + +static const struct i2c_device_id smiapp_id_table[] = { + { SMIAPP_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, smiapp_id_table); + +static const struct dev_pm_ops smiapp_pm_ops = { + .suspend = smiapp_suspend, + .resume = smiapp_resume, +}; + +static struct i2c_driver smiapp_i2c_driver = { + .driver = { + .name = SMIAPP_NAME, + .pm = &smiapp_pm_ops, + }, + .probe = smiapp_probe, + .remove = __exit_p(smiapp_remove), + .id_table = smiapp_id_table, +}; + +module_i2c_driver(smiapp_i2c_driver); + +MODULE_AUTHOR("Sakari Ailus "); +MODULE_DESCRIPTION("Generic SMIA/SMIA++ camera module driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/smiapp/smiapp-limits.c b/drivers/media/i2c/smiapp/smiapp-limits.c new file mode 100644 index 00000000000..fb2f81ad8c3 --- /dev/null +++ b/drivers/media/i2c/smiapp/smiapp-limits.c @@ -0,0 +1,132 @@ +/* + * drivers/media/i2c/smiapp/smiapp-limits.c + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "smiapp.h" + +struct smiapp_reg_limits smiapp_reg_limits[] = { + { SMIAPP_REG_U16_ANALOGUE_GAIN_CAPABILITY, "analogue_gain_capability" }, /* 0 */ + { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MIN, "analogue_gain_code_min" }, + { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MAX, "analogue_gain_code_max" }, + { SMIAPP_REG_U8_THS_ZERO_MIN, "ths_zero_min" }, + { SMIAPP_REG_U8_TCLK_TRAIL_MIN, "tclk_trail_min" }, + { SMIAPP_REG_U16_INTEGRATION_TIME_CAPABILITY, "integration_time_capability" }, /* 5 */ + { SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MIN, "coarse_integration_time_min" }, + { SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MAX_MARGIN, "coarse_integration_time_max_margin" }, + { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN, "fine_integration_time_min" }, + { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN, "fine_integration_time_max_margin" }, + { SMIAPP_REG_U16_DIGITAL_GAIN_CAPABILITY, "digital_gain_capability" }, /* 10 */ + { SMIAPP_REG_U16_DIGITAL_GAIN_MIN, "digital_gain_min" }, + { SMIAPP_REG_U16_DIGITAL_GAIN_MAX, "digital_gain_max" }, + { SMIAPP_REG_F32_MIN_EXT_CLK_FREQ_HZ, "min_ext_clk_freq_hz" }, + { SMIAPP_REG_F32_MAX_EXT_CLK_FREQ_HZ, "max_ext_clk_freq_hz" }, + { SMIAPP_REG_U16_MIN_PRE_PLL_CLK_DIV, "min_pre_pll_clk_div" }, /* 15 */ + { SMIAPP_REG_U16_MAX_PRE_PLL_CLK_DIV, "max_pre_pll_clk_div" }, + { SMIAPP_REG_F32_MIN_PLL_IP_FREQ_HZ, "min_pll_ip_freq_hz" }, + { SMIAPP_REG_F32_MAX_PLL_IP_FREQ_HZ, "max_pll_ip_freq_hz" }, + { SMIAPP_REG_U16_MIN_PLL_MULTIPLIER, "min_pll_multiplier" }, + { SMIAPP_REG_U16_MAX_PLL_MULTIPLIER, "max_pll_multiplier" }, /* 20 */ + { SMIAPP_REG_F32_MIN_PLL_OP_FREQ_HZ, "min_pll_op_freq_hz" }, + { SMIAPP_REG_F32_MAX_PLL_OP_FREQ_HZ, "max_pll_op_freq_hz" }, + { SMIAPP_REG_U16_MIN_VT_SYS_CLK_DIV, "min_vt_sys_clk_div" }, + { SMIAPP_REG_U16_MAX_VT_SYS_CLK_DIV, "max_vt_sys_clk_div" }, + { SMIAPP_REG_F32_MIN_VT_SYS_CLK_FREQ_HZ, "min_vt_sys_clk_freq_hz" }, /* 25 */ + { SMIAPP_REG_F32_MAX_VT_SYS_CLK_FREQ_HZ, "max_vt_sys_clk_freq_hz" }, + { SMIAPP_REG_F32_MIN_VT_PIX_CLK_FREQ_HZ, "min_vt_pix_clk_freq_hz" }, + { SMIAPP_REG_F32_MAX_VT_PIX_CLK_FREQ_HZ, "max_vt_pix_clk_freq_hz" }, + { SMIAPP_REG_U16_MIN_VT_PIX_CLK_DIV, "min_vt_pix_clk_div" }, + { SMIAPP_REG_U16_MAX_VT_PIX_CLK_DIV, "max_vt_pix_clk_div" }, /* 30 */ + { SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES, "min_frame_length_lines" }, + { SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES, "max_frame_length_lines" }, + { SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK, "min_line_length_pck" }, + { SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK, "max_line_length_pck" }, + { SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK, "min_line_blanking_pck" }, /* 35 */ + { SMIAPP_REG_U16_MIN_FRAME_BLANKING_LINES, "min_frame_blanking_lines" }, + { SMIAPP_REG_U8_MIN_LINE_LENGTH_PCK_STEP_SIZE, "min_line_length_pck_step_size" }, + { SMIAPP_REG_U16_MIN_OP_SYS_CLK_DIV, "min_op_sys_clk_div" }, + { SMIAPP_REG_U16_MAX_OP_SYS_CLK_DIV, "max_op_sys_clk_div" }, + { SMIAPP_REG_F32_MIN_OP_SYS_CLK_FREQ_HZ, "min_op_sys_clk_freq_hz" }, /* 40 */ + { SMIAPP_REG_F32_MAX_OP_SYS_CLK_FREQ_HZ, "max_op_sys_clk_freq_hz" }, + { SMIAPP_REG_U16_MIN_OP_PIX_CLK_DIV, "min_op_pix_clk_div" }, + { SMIAPP_REG_U16_MAX_OP_PIX_CLK_DIV, "max_op_pix_clk_div" }, + { SMIAPP_REG_F32_MIN_OP_PIX_CLK_FREQ_HZ, "min_op_pix_clk_freq_hz" }, + { SMIAPP_REG_F32_MAX_OP_PIX_CLK_FREQ_HZ, "max_op_pix_clk_freq_hz" }, /* 45 */ + { SMIAPP_REG_U16_X_ADDR_MIN, "x_addr_min" }, + { SMIAPP_REG_U16_Y_ADDR_MIN, "y_addr_min" }, + { SMIAPP_REG_U16_X_ADDR_MAX, "x_addr_max" }, + { SMIAPP_REG_U16_Y_ADDR_MAX, "y_addr_max" }, + { SMIAPP_REG_U16_MIN_X_OUTPUT_SIZE, "min_x_output_size" }, /* 50 */ + { SMIAPP_REG_U16_MIN_Y_OUTPUT_SIZE, "min_y_output_size" }, + { SMIAPP_REG_U16_MAX_X_OUTPUT_SIZE, "max_x_output_size" }, + { SMIAPP_REG_U16_MAX_Y_OUTPUT_SIZE, "max_y_output_size" }, + { SMIAPP_REG_U16_MIN_EVEN_INC, "min_even_inc" }, + { SMIAPP_REG_U16_MAX_EVEN_INC, "max_even_inc" }, /* 55 */ + { SMIAPP_REG_U16_MIN_ODD_INC, "min_odd_inc" }, + { SMIAPP_REG_U16_MAX_ODD_INC, "max_odd_inc" }, + { SMIAPP_REG_U16_SCALING_CAPABILITY, "scaling_capability" }, + { SMIAPP_REG_U16_SCALER_M_MIN, "scaler_m_min" }, + { SMIAPP_REG_U16_SCALER_M_MAX, "scaler_m_max" }, /* 60 */ + { SMIAPP_REG_U16_SCALER_N_MIN, "scaler_n_min" }, + { SMIAPP_REG_U16_SCALER_N_MAX, "scaler_n_max" }, + { SMIAPP_REG_U16_SPATIAL_SAMPLING_CAPABILITY, "spatial_sampling_capability" }, + { SMIAPP_REG_U8_DIGITAL_CROP_CAPABILITY, "digital_crop_capability" }, + { SMIAPP_REG_U16_COMPRESSION_CAPABILITY, "compression_capability" }, /* 65 */ + { SMIAPP_REG_U8_FIFO_SUPPORT_CAPABILITY, "fifo_support_capability" }, + { SMIAPP_REG_U8_DPHY_CTRL_CAPABILITY, "dphy_ctrl_capability" }, + { SMIAPP_REG_U8_CSI_LANE_MODE_CAPABILITY, "csi_lane_mode_capability" }, + { SMIAPP_REG_U8_CSI_SIGNALLING_MODE_CAPABILITY, "csi_signalling_mode_capability" }, + { SMIAPP_REG_U8_FAST_STANDBY_CAPABILITY, "fast_standby_capability" }, /* 70 */ + { SMIAPP_REG_U8_CCI_ADDRESS_CONTROL_CAPABILITY, "cci_address_control_capability" }, + { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS, "max_per_lane_bitrate_1_lane_mode_mbps" }, + { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS, "max_per_lane_bitrate_2_lane_mode_mbps" }, + { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS, "max_per_lane_bitrate_3_lane_mode_mbps" }, + { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS, "max_per_lane_bitrate_4_lane_mode_mbps" }, /* 75 */ + { SMIAPP_REG_U8_TEMP_SENSOR_CAPABILITY, "temp_sensor_capability" }, + { SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES_BIN, "min_frame_length_lines_bin" }, + { SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES_BIN, "max_frame_length_lines_bin" }, + { SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK_BIN, "min_line_length_pck_bin" }, + { SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK_BIN, "max_line_length_pck_bin" }, /* 80 */ + { SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK_BIN, "min_line_blanking_pck_bin" }, + { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN_BIN, "fine_integration_time_min_bin" }, + { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN, "fine_integration_time_max_margin_bin" }, + { SMIAPP_REG_U8_BINNING_CAPABILITY, "binning_capability" }, + { SMIAPP_REG_U8_BINNING_WEIGHTING_CAPABILITY, "binning_weighting_capability" }, /* 85 */ + { SMIAPP_REG_U8_DATA_TRANSFER_IF_CAPABILITY, "data_transfer_if_capability" }, + { SMIAPP_REG_U8_SHADING_CORRECTION_CAPABILITY, "shading_correction_capability" }, + { SMIAPP_REG_U8_GREEN_IMBALANCE_CAPABILITY, "green_imbalance_capability" }, + { SMIAPP_REG_U8_BLACK_LEVEL_CAPABILITY, "black_level_capability" }, + { SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_CAPABILITY, "module_specific_correction_capability" }, /* 90 */ + { SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY, "defect_correction_capability" }, + { SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY_2, "defect_correction_capability_2" }, + { SMIAPP_REG_U8_EDOF_CAPABILITY, "edof_capability" }, + { SMIAPP_REG_U8_COLOUR_FEEDBACK_CAPABILITY, "colour_feedback_capability" }, + { SMIAPP_REG_U8_ESTIMATION_MODE_CAPABILITY, "estimation_mode_capability" }, /* 95 */ + { SMIAPP_REG_U8_ESTIMATION_ZONE_CAPABILITY, "estimation_zone_capability" }, + { SMIAPP_REG_U16_CAPABILITY_TRDY_MIN, "capability_trdy_min" }, + { SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, "flash_mode_capability" }, + { SMIAPP_REG_U8_ACTUATOR_CAPABILITY, "actuator_capability" }, + { SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_1, "bracketing_lut_capability_1" }, /* 100 */ + { SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_2, "bracketing_lut_capability_2" }, + { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_STEP, "analogue_gain_code_step" }, + { 0, NULL }, +}; diff --git a/drivers/media/i2c/smiapp/smiapp-limits.h b/drivers/media/i2c/smiapp/smiapp-limits.h new file mode 100644 index 00000000000..9ae765e23ea --- /dev/null +++ b/drivers/media/i2c/smiapp/smiapp-limits.h @@ -0,0 +1,128 @@ +/* + * drivers/media/i2c/smiapp/smiapp-limits.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#define SMIAPP_LIMIT_ANALOGUE_GAIN_CAPABILITY 0 +#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN 1 +#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX 2 +#define SMIAPP_LIMIT_THS_ZERO_MIN 3 +#define SMIAPP_LIMIT_TCLK_TRAIL_MIN 4 +#define SMIAPP_LIMIT_INTEGRATION_TIME_CAPABILITY 5 +#define SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MIN 6 +#define SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MAX_MARGIN 7 +#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN 8 +#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN 9 +#define SMIAPP_LIMIT_DIGITAL_GAIN_CAPABILITY 10 +#define SMIAPP_LIMIT_DIGITAL_GAIN_MIN 11 +#define SMIAPP_LIMIT_DIGITAL_GAIN_MAX 12 +#define SMIAPP_LIMIT_MIN_EXT_CLK_FREQ_HZ 13 +#define SMIAPP_LIMIT_MAX_EXT_CLK_FREQ_HZ 14 +#define SMIAPP_LIMIT_MIN_PRE_PLL_CLK_DIV 15 +#define SMIAPP_LIMIT_MAX_PRE_PLL_CLK_DIV 16 +#define SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ 17 +#define SMIAPP_LIMIT_MAX_PLL_IP_FREQ_HZ 18 +#define SMIAPP_LIMIT_MIN_PLL_MULTIPLIER 19 +#define SMIAPP_LIMIT_MAX_PLL_MULTIPLIER 20 +#define SMIAPP_LIMIT_MIN_PLL_OP_FREQ_HZ 21 +#define SMIAPP_LIMIT_MAX_PLL_OP_FREQ_HZ 22 +#define SMIAPP_LIMIT_MIN_VT_SYS_CLK_DIV 23 +#define SMIAPP_LIMIT_MAX_VT_SYS_CLK_DIV 24 +#define SMIAPP_LIMIT_MIN_VT_SYS_CLK_FREQ_HZ 25 +#define SMIAPP_LIMIT_MAX_VT_SYS_CLK_FREQ_HZ 26 +#define SMIAPP_LIMIT_MIN_VT_PIX_CLK_FREQ_HZ 27 +#define SMIAPP_LIMIT_MAX_VT_PIX_CLK_FREQ_HZ 28 +#define SMIAPP_LIMIT_MIN_VT_PIX_CLK_DIV 29 +#define SMIAPP_LIMIT_MAX_VT_PIX_CLK_DIV 30 +#define SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES 31 +#define SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES 32 +#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK 33 +#define SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK 34 +#define SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK 35 +#define SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES 36 +#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_STEP_SIZE 37 +#define SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV 38 +#define SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV 39 +#define SMIAPP_LIMIT_MIN_OP_SYS_CLK_FREQ_HZ 40 +#define SMIAPP_LIMIT_MAX_OP_SYS_CLK_FREQ_HZ 41 +#define SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV 42 +#define SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV 43 +#define SMIAPP_LIMIT_MIN_OP_PIX_CLK_FREQ_HZ 44 +#define SMIAPP_LIMIT_MAX_OP_PIX_CLK_FREQ_HZ 45 +#define SMIAPP_LIMIT_X_ADDR_MIN 46 +#define SMIAPP_LIMIT_Y_ADDR_MIN 47 +#define SMIAPP_LIMIT_X_ADDR_MAX 48 +#define SMIAPP_LIMIT_Y_ADDR_MAX 49 +#define SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE 50 +#define SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE 51 +#define SMIAPP_LIMIT_MAX_X_OUTPUT_SIZE 52 +#define SMIAPP_LIMIT_MAX_Y_OUTPUT_SIZE 53 +#define SMIAPP_LIMIT_MIN_EVEN_INC 54 +#define SMIAPP_LIMIT_MAX_EVEN_INC 55 +#define SMIAPP_LIMIT_MIN_ODD_INC 56 +#define SMIAPP_LIMIT_MAX_ODD_INC 57 +#define SMIAPP_LIMIT_SCALING_CAPABILITY 58 +#define SMIAPP_LIMIT_SCALER_M_MIN 59 +#define SMIAPP_LIMIT_SCALER_M_MAX 60 +#define SMIAPP_LIMIT_SCALER_N_MIN 61 +#define SMIAPP_LIMIT_SCALER_N_MAX 62 +#define SMIAPP_LIMIT_SPATIAL_SAMPLING_CAPABILITY 63 +#define SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY 64 +#define SMIAPP_LIMIT_COMPRESSION_CAPABILITY 65 +#define SMIAPP_LIMIT_FIFO_SUPPORT_CAPABILITY 66 +#define SMIAPP_LIMIT_DPHY_CTRL_CAPABILITY 67 +#define SMIAPP_LIMIT_CSI_LANE_MODE_CAPABILITY 68 +#define SMIAPP_LIMIT_CSI_SIGNALLING_MODE_CAPABILITY 69 +#define SMIAPP_LIMIT_FAST_STANDBY_CAPABILITY 70 +#define SMIAPP_LIMIT_CCI_ADDRESS_CONTROL_CAPABILITY 71 +#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS 72 +#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS 73 +#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS 74 +#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS 75 +#define SMIAPP_LIMIT_TEMP_SENSOR_CAPABILITY 76 +#define SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN 77 +#define SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN 78 +#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN 79 +#define SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN 80 +#define SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN 81 +#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN_BIN 82 +#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN 83 +#define SMIAPP_LIMIT_BINNING_CAPABILITY 84 +#define SMIAPP_LIMIT_BINNING_WEIGHTING_CAPABILITY 85 +#define SMIAPP_LIMIT_DATA_TRANSFER_IF_CAPABILITY 86 +#define SMIAPP_LIMIT_SHADING_CORRECTION_CAPABILITY 87 +#define SMIAPP_LIMIT_GREEN_IMBALANCE_CAPABILITY 88 +#define SMIAPP_LIMIT_BLACK_LEVEL_CAPABILITY 89 +#define SMIAPP_LIMIT_MODULE_SPECIFIC_CORRECTION_CAPABILITY 90 +#define SMIAPP_LIMIT_DEFECT_CORRECTION_CAPABILITY 91 +#define SMIAPP_LIMIT_DEFECT_CORRECTION_CAPABILITY_2 92 +#define SMIAPP_LIMIT_EDOF_CAPABILITY 93 +#define SMIAPP_LIMIT_COLOUR_FEEDBACK_CAPABILITY 94 +#define SMIAPP_LIMIT_ESTIMATION_MODE_CAPABILITY 95 +#define SMIAPP_LIMIT_ESTIMATION_ZONE_CAPABILITY 96 +#define SMIAPP_LIMIT_CAPABILITY_TRDY_MIN 97 +#define SMIAPP_LIMIT_FLASH_MODE_CAPABILITY 98 +#define SMIAPP_LIMIT_ACTUATOR_CAPABILITY 99 +#define SMIAPP_LIMIT_BRACKETING_LUT_CAPABILITY_1 100 +#define SMIAPP_LIMIT_BRACKETING_LUT_CAPABILITY_2 101 +#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_STEP 102 +#define SMIAPP_LIMIT_LAST 103 diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.c b/drivers/media/i2c/smiapp/smiapp-quirk.c new file mode 100644 index 00000000000..cf048128367 --- /dev/null +++ b/drivers/media/i2c/smiapp/smiapp-quirk.c @@ -0,0 +1,306 @@ +/* + * drivers/media/i2c/smiapp/smiapp-quirk.c + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include + +#include "smiapp.h" + +static int smiapp_write_8(struct smiapp_sensor *sensor, u16 reg, u8 val) +{ + return smiapp_write(sensor, (SMIA_REG_8BIT << 16) | reg, val); +} + +static int smiapp_write_8s(struct smiapp_sensor *sensor, + struct smiapp_reg_8 *regs, int len) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + for (; len > 0; len--, regs++) { + rval = smiapp_write_8(sensor, regs->reg, regs->val); + if (rval < 0) { + dev_err(&client->dev, + "error %d writing reg 0x%4.4x, val 0x%2.2x", + rval, regs->reg, regs->val); + return rval; + } + } + + return 0; +} + +void smiapp_replace_limit(struct smiapp_sensor *sensor, + u32 limit, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + + dev_dbg(&client->dev, "quirk: 0x%8.8x \"%s\" = %d, 0x%x\n", + smiapp_reg_limits[limit].addr, + smiapp_reg_limits[limit].what, val, val); + sensor->limits[limit] = val; +} + +int smiapp_replace_limit_at(struct smiapp_sensor *sensor, + u32 reg, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int i; + + for (i = 0; smiapp_reg_limits[i].addr; i++) { + if ((smiapp_reg_limits[i].addr & 0xffff) != reg) + continue; + + smiapp_replace_limit(sensor, i, val); + + return 0; + } + + dev_dbg(&client->dev, "quirk: bad register 0x%4.4x\n", reg); + + return -EINVAL; +} + +bool smiapp_quirk_reg(struct smiapp_sensor *sensor, + u32 reg, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + const struct smia_reg *sreg; + + if (!sensor->minfo.quirk) + return false; + + sreg = sensor->minfo.quirk->regs; + + if (!sreg) + return false; + + while (sreg->type) { + u16 type = reg >> 16; + u16 reg16 = reg; + + if (sreg->type != type || sreg->reg != reg16) { + sreg++; + continue; + } + + switch ((u8)type) { + case SMIA_REG_8BIT: + dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%2.2x\n", + reg, sreg->val); + break; + case SMIA_REG_16BIT: + dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%4.4x\n", + reg, sreg->val); + break; + case SMIA_REG_32BIT: + dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%8.8x\n", + reg, sreg->val); + break; + } + + *val = sreg->val; + + return true; + } + + return false; +} + +static int jt8ew9_limits(struct smiapp_sensor *sensor) +{ + if (sensor->minfo.revision_number_major < 0x03) + sensor->frame_skip = 1; + + /* Below 24 gain doesn't have effect at all, */ + /* but ~59 is needed for full dynamic range */ + smiapp_replace_limit(sensor, SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN, 59); + smiapp_replace_limit( + sensor, SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX, 6000); + + return 0; +} + +static int jt8ew9_post_poweron(struct smiapp_sensor *sensor) +{ + struct smiapp_reg_8 regs[] = { + { 0x30a3, 0xd8 }, /* Output port control : LVDS ports only */ + { 0x30ae, 0x00 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */ + { 0x30af, 0xd0 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */ + { 0x322d, 0x04 }, /* Adjusting Processing Image Size to Scaler Toshiba Recommendation Setting */ + { 0x3255, 0x0f }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */ + { 0x3256, 0x15 }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */ + { 0x3258, 0x70 }, /* Analog Gain Control Toshiba Recommendation Setting */ + { 0x3259, 0x70 }, /* Analog Gain Control Toshiba Recommendation Setting */ + { 0x325f, 0x7c }, /* Analog Gain Control Toshiba Recommendation Setting */ + { 0x3302, 0x06 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ + { 0x3304, 0x00 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ + { 0x3307, 0x22 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ + { 0x3308, 0x8d }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ + { 0x331e, 0x0f }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x3320, 0x30 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x3321, 0x11 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x3322, 0x98 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x3323, 0x64 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x3325, 0x83 }, /* Read Out Timing Control Toshiba Recommendation Setting */ + { 0x3330, 0x18 }, /* Read Out Timing Control Toshiba Recommendation Setting */ + { 0x333c, 0x01 }, /* Read Out Timing Control Toshiba Recommendation Setting */ + { 0x3345, 0x2f }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x33de, 0x38 }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */ + /* Taken from v03. No idea what the rest are. */ + { 0x32e0, 0x05 }, + { 0x32e1, 0x05 }, + { 0x32e2, 0x04 }, + { 0x32e5, 0x04 }, + { 0x32e6, 0x04 }, + + }; + + return smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs)); +} + +const struct smiapp_quirk smiapp_jt8ew9_quirk = { + .limits = jt8ew9_limits, + .post_poweron = jt8ew9_post_poweron, +}; + +static int imx125es_post_poweron(struct smiapp_sensor *sensor) +{ + /* Taken from v02. No idea what the other two are. */ + struct smiapp_reg_8 regs[] = { + /* + * 0x3302: clk during frame blanking: + * 0x00 - HS mode, 0x01 - LP11 + */ + { 0x3302, 0x01 }, + { 0x302d, 0x00 }, + { 0x3b08, 0x8c }, + }; + + return smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs)); +} + +const struct smiapp_quirk smiapp_imx125es_quirk = { + .post_poweron = imx125es_post_poweron, +}; + +static int jt8ev1_limits(struct smiapp_sensor *sensor) +{ + smiapp_replace_limit(sensor, SMIAPP_LIMIT_X_ADDR_MAX, 4271); + smiapp_replace_limit(sensor, + SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN, 184); + + return 0; +} + +static int jt8ev1_post_poweron(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + struct smiapp_reg_8 regs[] = { + { 0x3031, 0xcd }, /* For digital binning (EQ_MONI) */ + { 0x30a3, 0xd0 }, /* FLASH STROBE enable */ + { 0x3237, 0x00 }, /* For control of pulse timing for ADC */ + { 0x3238, 0x43 }, + { 0x3301, 0x06 }, /* For analog bias for sensor */ + { 0x3302, 0x06 }, + { 0x3304, 0x00 }, + { 0x3305, 0x88 }, + { 0x332a, 0x14 }, + { 0x332c, 0x6b }, + { 0x3336, 0x01 }, + { 0x333f, 0x1f }, + { 0x3355, 0x00 }, + { 0x3356, 0x20 }, + { 0x33bf, 0x20 }, /* Adjust the FBC speed */ + { 0x33c9, 0x20 }, + { 0x33ce, 0x30 }, /* Adjust the parameter for logic function */ + { 0x33cf, 0xec }, /* For Black sun */ + { 0x3328, 0x80 }, /* Ugh. No idea what's this. */ + }; + + struct smiapp_reg_8 regs_96[] = { + { 0x30ae, 0x00 }, /* For control of ADC clock */ + { 0x30af, 0xd0 }, + { 0x30b0, 0x01 }, + }; + + rval = smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs)); + if (rval < 0) + return rval; + + switch (sensor->platform_data->ext_clk) { + case 9600000: + return smiapp_write_8s(sensor, regs_96, + ARRAY_SIZE(regs_96)); + default: + dev_warn(&client->dev, "no MSRs for %d Hz ext_clk\n", + sensor->platform_data->ext_clk); + return 0; + } +} + +static int jt8ev1_pre_streamon(struct smiapp_sensor *sensor) +{ + return smiapp_write_8(sensor, 0x3328, 0x00); +} + +static int jt8ev1_post_streamoff(struct smiapp_sensor *sensor) +{ + int rval; + + /* Workaround: allows fast standby to work properly */ + rval = smiapp_write_8(sensor, 0x3205, 0x04); + if (rval < 0) + return rval; + + /* Wait for 1 ms + one line => 2 ms is likely enough */ + usleep_range(2000, 2000); + + /* Restore it */ + rval = smiapp_write_8(sensor, 0x3205, 0x00); + if (rval < 0) + return rval; + + return smiapp_write_8(sensor, 0x3328, 0x80); +} + +const struct smiapp_quirk smiapp_jt8ev1_quirk = { + .limits = jt8ev1_limits, + .post_poweron = jt8ev1_post_poweron, + .pre_streamon = jt8ev1_pre_streamon, + .post_streamoff = jt8ev1_post_streamoff, + .flags = SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE, +}; + +static int tcm8500md_limits(struct smiapp_sensor *sensor) +{ + smiapp_replace_limit(sensor, SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ, 2700000); + + return 0; +} + +const struct smiapp_quirk smiapp_tcm8500md_quirk = { + .limits = tcm8500md_limits, +}; diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.h b/drivers/media/i2c/smiapp/smiapp-quirk.h new file mode 100644 index 00000000000..86fd3e8bfb0 --- /dev/null +++ b/drivers/media/i2c/smiapp/smiapp-quirk.h @@ -0,0 +1,83 @@ +/* + * drivers/media/i2c/smiapp/smiapp-quirk.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __SMIAPP_QUIRK__ +#define __SMIAPP_QUIRK__ + +struct smiapp_sensor; + +/** + * struct smiapp_quirk - quirks for sensors that deviate from SMIA++ standard + * + * @limits: Replace sensor->limits with values which can't be read from + * sensor registers. Called the first time the sensor is powered up. + * @post_poweron: Called always after the sensor has been fully powered on. + * @pre_streamon: Called just before streaming is enabled. + * @post_streamon: Called right after stopping streaming. + */ +struct smiapp_quirk { + int (*limits)(struct smiapp_sensor *sensor); + int (*post_poweron)(struct smiapp_sensor *sensor); + int (*pre_streamon)(struct smiapp_sensor *sensor); + int (*post_streamoff)(struct smiapp_sensor *sensor); + const struct smia_reg *regs; + unsigned long flags; +}; + +/* op pix clock is for all lanes in total normally */ +#define SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0) +#define SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY (1 << 1) + +struct smiapp_reg_8 { + u16 reg; + u8 val; +}; + +void smiapp_replace_limit(struct smiapp_sensor *sensor, + u32 limit, u32 val); +bool smiapp_quirk_reg(struct smiapp_sensor *sensor, + u32 reg, u32 *val); + +#define SMIAPP_MK_QUIRK_REG(_reg, _val) \ + { \ + .type = (_reg >> 16), \ + .reg = (u16)_reg, \ + .val = _val, \ + } + +#define smiapp_call_quirk(_sensor, _quirk, ...) \ + (_sensor->minfo.quirk && \ + _sensor->minfo.quirk->_quirk ? \ + _sensor->minfo.quirk->_quirk(_sensor, ##__VA_ARGS__) : 0) + +#define smiapp_needs_quirk(_sensor, _quirk) \ + (_sensor->minfo.quirk ? \ + _sensor->minfo.quirk->flags & _quirk : 0) + +extern const struct smiapp_quirk smiapp_jt8ev1_quirk; +extern const struct smiapp_quirk smiapp_imx125es_quirk; +extern const struct smiapp_quirk smiapp_jt8ew9_quirk; +extern const struct smiapp_quirk smiapp_tcm8500md_quirk; + +#endif /* __SMIAPP_QUIRK__ */ diff --git a/drivers/media/i2c/smiapp/smiapp-reg-defs.h b/drivers/media/i2c/smiapp/smiapp-reg-defs.h new file mode 100644 index 00000000000..defa7c5adeb --- /dev/null +++ b/drivers/media/i2c/smiapp/smiapp-reg-defs.h @@ -0,0 +1,503 @@ +/* + * drivers/media/i2c/smiapp/smiapp-reg-defs.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ +#define SMIAPP_REG_MK_U8(r) ((SMIA_REG_8BIT << 16) | (r)) +#define SMIAPP_REG_MK_U16(r) ((SMIA_REG_16BIT << 16) | (r)) +#define SMIAPP_REG_MK_U32(r) ((SMIA_REG_32BIT << 16) | (r)) + +#define SMIAPP_REG_MK_F32(r) (SMIA_REG_FLAG_FLOAT | (SMIA_REG_32BIT << 16) | (r)) + +#define SMIAPP_REG_U16_MODEL_ID SMIAPP_REG_MK_U16(0x0000) +#define SMIAPP_REG_U8_REVISION_NUMBER_MAJOR SMIAPP_REG_MK_U8(0x0002) +#define SMIAPP_REG_U8_MANUFACTURER_ID SMIAPP_REG_MK_U8(0x0003) +#define SMIAPP_REG_U8_SMIA_VERSION SMIAPP_REG_MK_U8(0x0004) +#define SMIAPP_REG_U8_FRAME_COUNT SMIAPP_REG_MK_U8(0x0005) +#define SMIAPP_REG_U8_PIXEL_ORDER SMIAPP_REG_MK_U8(0x0006) +#define SMIAPP_REG_U16_DATA_PEDESTAL SMIAPP_REG_MK_U16(0x0008) +#define SMIAPP_REG_U8_PIXEL_DEPTH SMIAPP_REG_MK_U8(0x000c) +#define SMIAPP_REG_U8_REVISION_NUMBER_MINOR SMIAPP_REG_MK_U8(0x0010) +#define SMIAPP_REG_U8_SMIAPP_VERSION SMIAPP_REG_MK_U8(0x0011) +#define SMIAPP_REG_U8_MODULE_DATE_YEAR SMIAPP_REG_MK_U8(0x0012) +#define SMIAPP_REG_U8_MODULE_DATE_MONTH SMIAPP_REG_MK_U8(0x0013) +#define SMIAPP_REG_U8_MODULE_DATE_DAY SMIAPP_REG_MK_U8(0x0014) +#define SMIAPP_REG_U8_MODULE_DATE_PHASE SMIAPP_REG_MK_U8(0x0015) +#define SMIAPP_REG_U16_SENSOR_MODEL_ID SMIAPP_REG_MK_U16(0x0016) +#define SMIAPP_REG_U8_SENSOR_REVISION_NUMBER SMIAPP_REG_MK_U8(0x0018) +#define SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID SMIAPP_REG_MK_U8(0x0019) +#define SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION SMIAPP_REG_MK_U8(0x001a) +#define SMIAPP_REG_U32_SERIAL_NUMBER SMIAPP_REG_MK_U32(0x001c) +#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE SMIAPP_REG_MK_U8(0x0040) +#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE SMIAPP_REG_MK_U8(0x0041) +#define SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(n) SMIAPP_REG_MK_U16(0x0042 + ((n) << 1)) /* 0 <= n <= 14 */ +#define SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(n) SMIAPP_REG_MK_U32(0x0060 + ((n) << 2)) /* 0 <= n <= 7 */ +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CAPABILITY SMIAPP_REG_MK_U16(0x0080) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MIN SMIAPP_REG_MK_U16(0x0084) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MAX SMIAPP_REG_MK_U16(0x0086) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_STEP SMIAPP_REG_MK_U16(0x0088) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_TYPE SMIAPP_REG_MK_U16(0x008a) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_M0 SMIAPP_REG_MK_U16(0x008c) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_C0 SMIAPP_REG_MK_U16(0x008e) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_M1 SMIAPP_REG_MK_U16(0x0090) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_C1 SMIAPP_REG_MK_U16(0x0092) +#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE SMIAPP_REG_MK_U8(0x00c0) +#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_SUBTYPE SMIAPP_REG_MK_U8(0x00c1) +#define SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(n) SMIAPP_REG_MK_U16(0x00c2 + ((n) << 1)) +#define SMIAPP_REG_U8_MODE_SELECT SMIAPP_REG_MK_U8(0x0100) +#define SMIAPP_REG_U8_IMAGE_ORIENTATION SMIAPP_REG_MK_U8(0x0101) +#define SMIAPP_REG_U8_SOFTWARE_RESET SMIAPP_REG_MK_U8(0x0103) +#define SMIAPP_REG_U8_GROUPED_PARAMETER_HOLD SMIAPP_REG_MK_U8(0x0104) +#define SMIAPP_REG_U8_MASK_CORRUPTED_FRAMES SMIAPP_REG_MK_U8(0x0105) +#define SMIAPP_REG_U8_FAST_STANDBY_CTRL SMIAPP_REG_MK_U8(0x0106) +#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL SMIAPP_REG_MK_U8(0x0107) +#define SMIAPP_REG_U8_2ND_CCI_IF_CONTROL SMIAPP_REG_MK_U8(0x0108) +#define SMIAPP_REG_U8_2ND_CCI_ADDRESS_CONTROL SMIAPP_REG_MK_U8(0x0109) +#define SMIAPP_REG_U8_CSI_CHANNEL_IDENTIFIER SMIAPP_REG_MK_U8(0x0110) +#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE SMIAPP_REG_MK_U8(0x0111) +#define SMIAPP_REG_U16_CSI_DATA_FORMAT SMIAPP_REG_MK_U16(0x0112) +#define SMIAPP_REG_U8_CSI_LANE_MODE SMIAPP_REG_MK_U8(0x0114) +#define SMIAPP_REG_U8_CSI2_10_TO_8_DT SMIAPP_REG_MK_U8(0x0115) +#define SMIAPP_REG_U8_CSI2_10_TO_7_DT SMIAPP_REG_MK_U8(0x0116) +#define SMIAPP_REG_U8_CSI2_10_TO_6_DT SMIAPP_REG_MK_U8(0x0117) +#define SMIAPP_REG_U8_CSI2_12_TO_8_DT SMIAPP_REG_MK_U8(0x0118) +#define SMIAPP_REG_U8_CSI2_12_TO_7_DT SMIAPP_REG_MK_U8(0x0119) +#define SMIAPP_REG_U8_CSI2_12_TO_6_DT SMIAPP_REG_MK_U8(0x011a) +#define SMIAPP_REG_U8_CSI2_14_TO_10_DT SMIAPP_REG_MK_U8(0x011b) +#define SMIAPP_REG_U8_CSI2_14_TO_8_DT SMIAPP_REG_MK_U8(0x011c) +#define SMIAPP_REG_U8_CSI2_16_TO_10_DT SMIAPP_REG_MK_U8(0x011d) +#define SMIAPP_REG_U8_CSI2_16_TO_8_DT SMIAPP_REG_MK_U8(0x011e) +#define SMIAPP_REG_U8_GAIN_MODE SMIAPP_REG_MK_U8(0x0120) +#define SMIAPP_REG_U16_VANA_VOLTAGE SMIAPP_REG_MK_U16(0x0130) +#define SMIAPP_REG_U16_VDIG_VOLTAGE SMIAPP_REG_MK_U16(0x0132) +#define SMIAPP_REG_U16_VIO_VOLTAGE SMIAPP_REG_MK_U16(0x0134) +#define SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ SMIAPP_REG_MK_U16(0x0136) +#define SMIAPP_REG_U8_TEMP_SENSOR_CONTROL SMIAPP_REG_MK_U8(0x0138) +#define SMIAPP_REG_U8_TEMP_SENSOR_MODE SMIAPP_REG_MK_U8(0x0139) +#define SMIAPP_REG_U8_TEMP_SENSOR_OUTPUT SMIAPP_REG_MK_U8(0x013a) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME SMIAPP_REG_MK_U16(0x0200) +#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME SMIAPP_REG_MK_U16(0x0202) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL SMIAPP_REG_MK_U16(0x0204) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENR SMIAPP_REG_MK_U16(0x0206) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_RED SMIAPP_REG_MK_U16(0x0208) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_BLUE SMIAPP_REG_MK_U16(0x020a) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENB SMIAPP_REG_MK_U16(0x020c) +#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENR SMIAPP_REG_MK_U16(0x020e) +#define SMIAPP_REG_U16_DIGITAL_GAIN_RED SMIAPP_REG_MK_U16(0x0210) +#define SMIAPP_REG_U16_DIGITAL_GAIN_BLUE SMIAPP_REG_MK_U16(0x0212) +#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENB SMIAPP_REG_MK_U16(0x0214) +#define SMIAPP_REG_U16_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x0300) +#define SMIAPP_REG_U16_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x0302) +#define SMIAPP_REG_U16_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x0304) +#define SMIAPP_REG_U16_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x0306) +#define SMIAPP_REG_U16_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x0308) +#define SMIAPP_REG_U16_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x030a) +#define SMIAPP_REG_U16_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x0340) +#define SMIAPP_REG_U16_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x0342) +#define SMIAPP_REG_U16_X_ADDR_START SMIAPP_REG_MK_U16(0x0344) +#define SMIAPP_REG_U16_Y_ADDR_START SMIAPP_REG_MK_U16(0x0346) +#define SMIAPP_REG_U16_X_ADDR_END SMIAPP_REG_MK_U16(0x0348) +#define SMIAPP_REG_U16_Y_ADDR_END SMIAPP_REG_MK_U16(0x034a) +#define SMIAPP_REG_U16_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x034c) +#define SMIAPP_REG_U16_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x034e) +#define SMIAPP_REG_U16_X_EVEN_INC SMIAPP_REG_MK_U16(0x0380) +#define SMIAPP_REG_U16_X_ODD_INC SMIAPP_REG_MK_U16(0x0382) +#define SMIAPP_REG_U16_Y_EVEN_INC SMIAPP_REG_MK_U16(0x0384) +#define SMIAPP_REG_U16_Y_ODD_INC SMIAPP_REG_MK_U16(0x0386) +#define SMIAPP_REG_U16_SCALING_MODE SMIAPP_REG_MK_U16(0x0400) +#define SMIAPP_REG_U16_SPATIAL_SAMPLING SMIAPP_REG_MK_U16(0x0402) +#define SMIAPP_REG_U16_SCALE_M SMIAPP_REG_MK_U16(0x0404) +#define SMIAPP_REG_U16_SCALE_N SMIAPP_REG_MK_U16(0x0406) +#define SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET SMIAPP_REG_MK_U16(0x0408) +#define SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET SMIAPP_REG_MK_U16(0x040a) +#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH SMIAPP_REG_MK_U16(0x040c) +#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT SMIAPP_REG_MK_U16(0x040e) +#define SMIAPP_REG_U16_COMPRESSION_MODE SMIAPP_REG_MK_U16(0x0500) +#define SMIAPP_REG_U16_TEST_PATTERN_MODE SMIAPP_REG_MK_U16(0x0600) +#define SMIAPP_REG_U16_TEST_DATA_RED SMIAPP_REG_MK_U16(0x0602) +#define SMIAPP_REG_U16_TEST_DATA_GREENR SMIAPP_REG_MK_U16(0x0604) +#define SMIAPP_REG_U16_TEST_DATA_BLUE SMIAPP_REG_MK_U16(0x0606) +#define SMIAPP_REG_U16_TEST_DATA_GREENB SMIAPP_REG_MK_U16(0x0608) +#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_WIDTH SMIAPP_REG_MK_U16(0x060a) +#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_POSITION SMIAPP_REG_MK_U16(0x060c) +#define SMIAPP_REG_U16_VERTICAL_CURSOR_WIDTH SMIAPP_REG_MK_U16(0x060e) +#define SMIAPP_REG_U16_VERTICAL_CURSOR_POSITION SMIAPP_REG_MK_U16(0x0610) +#define SMIAPP_REG_U16_FIFO_WATER_MARK_PIXELS SMIAPP_REG_MK_U16(0x0700) +#define SMIAPP_REG_U8_TCLK_POST SMIAPP_REG_MK_U8(0x0800) +#define SMIAPP_REG_U8_THS_PREPARE SMIAPP_REG_MK_U8(0x0801) +#define SMIAPP_REG_U8_THS_ZERO_MIN SMIAPP_REG_MK_U8(0x0802) +#define SMIAPP_REG_U8_THS_TRAIL SMIAPP_REG_MK_U8(0x0803) +#define SMIAPP_REG_U8_TCLK_TRAIL_MIN SMIAPP_REG_MK_U8(0x0804) +#define SMIAPP_REG_U8_TCLK_PREPARE SMIAPP_REG_MK_U8(0x0805) +#define SMIAPP_REG_U8_TCLK_ZERO SMIAPP_REG_MK_U8(0x0806) +#define SMIAPP_REG_U8_TLPX SMIAPP_REG_MK_U8(0x0807) +#define SMIAPP_REG_U8_DPHY_CTRL SMIAPP_REG_MK_U8(0x0808) +#define SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS SMIAPP_REG_MK_U32(0x0820) +#define SMIAPP_REG_U8_BINNING_MODE SMIAPP_REG_MK_U8(0x0900) +#define SMIAPP_REG_U8_BINNING_TYPE SMIAPP_REG_MK_U8(0x0901) +#define SMIAPP_REG_U8_BINNING_WEIGHTING SMIAPP_REG_MK_U8(0x0902) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL SMIAPP_REG_MK_U8(0x0a00) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS SMIAPP_REG_MK_U8(0x0a01) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT SMIAPP_REG_MK_U8(0x0a02) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 SMIAPP_REG_MK_U8(0x0a04) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_1 SMIAPP_REG_MK_U8(0x0a05) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_2 SMIAPP_REG_MK_U8(0x0a06) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_3 SMIAPP_REG_MK_U8(0x0a07) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_4 SMIAPP_REG_MK_U8(0x0a08) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_5 SMIAPP_REG_MK_U8(0x0a09) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_12 SMIAPP_REG_MK_U8(0x0a10) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_13 SMIAPP_REG_MK_U8(0x0a11) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_14 SMIAPP_REG_MK_U8(0x0a12) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_15 SMIAPP_REG_MK_U8(0x0a13) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_16 SMIAPP_REG_MK_U8(0x0a14) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_17 SMIAPP_REG_MK_U8(0x0a15) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_18 SMIAPP_REG_MK_U8(0x0a16) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_19 SMIAPP_REG_MK_U8(0x0a17) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_20 SMIAPP_REG_MK_U8(0x0a18) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_21 SMIAPP_REG_MK_U8(0x0a19) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_22 SMIAPP_REG_MK_U8(0x0a1a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_23 SMIAPP_REG_MK_U8(0x0a1b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_24 SMIAPP_REG_MK_U8(0x0a1c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_25 SMIAPP_REG_MK_U8(0x0a1d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_26 SMIAPP_REG_MK_U8(0x0a1e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_27 SMIAPP_REG_MK_U8(0x0a1f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_28 SMIAPP_REG_MK_U8(0x0a20) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_29 SMIAPP_REG_MK_U8(0x0a21) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_30 SMIAPP_REG_MK_U8(0x0a22) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_31 SMIAPP_REG_MK_U8(0x0a23) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_32 SMIAPP_REG_MK_U8(0x0a24) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_33 SMIAPP_REG_MK_U8(0x0a25) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_34 SMIAPP_REG_MK_U8(0x0a26) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_35 SMIAPP_REG_MK_U8(0x0a27) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_36 SMIAPP_REG_MK_U8(0x0a28) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_37 SMIAPP_REG_MK_U8(0x0a29) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_38 SMIAPP_REG_MK_U8(0x0a2a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_39 SMIAPP_REG_MK_U8(0x0a2b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_40 SMIAPP_REG_MK_U8(0x0a2c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_41 SMIAPP_REG_MK_U8(0x0a2d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_42 SMIAPP_REG_MK_U8(0x0a2e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_43 SMIAPP_REG_MK_U8(0x0a2f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_44 SMIAPP_REG_MK_U8(0x0a30) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_45 SMIAPP_REG_MK_U8(0x0a31) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_46 SMIAPP_REG_MK_U8(0x0a32) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_47 SMIAPP_REG_MK_U8(0x0a33) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_48 SMIAPP_REG_MK_U8(0x0a34) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_49 SMIAPP_REG_MK_U8(0x0a35) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_50 SMIAPP_REG_MK_U8(0x0a36) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_51 SMIAPP_REG_MK_U8(0x0a37) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_52 SMIAPP_REG_MK_U8(0x0a38) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_53 SMIAPP_REG_MK_U8(0x0a39) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_54 SMIAPP_REG_MK_U8(0x0a3a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_55 SMIAPP_REG_MK_U8(0x0a3b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_56 SMIAPP_REG_MK_U8(0x0a3c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_57 SMIAPP_REG_MK_U8(0x0a3d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_58 SMIAPP_REG_MK_U8(0x0a3e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_59 SMIAPP_REG_MK_U8(0x0a3f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_60 SMIAPP_REG_MK_U8(0x0a40) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_61 SMIAPP_REG_MK_U8(0x0a41) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_62 SMIAPP_REG_MK_U8(0x0a42) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_63 SMIAPP_REG_MK_U8(0x0a43) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_CTRL SMIAPP_REG_MK_U8(0x0a44) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_STATUS SMIAPP_REG_MK_U8(0x0a45) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_PAGE_SELECT SMIAPP_REG_MK_U8(0x0a46) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_0 SMIAPP_REG_MK_U8(0x0a48) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_1 SMIAPP_REG_MK_U8(0x0a49) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_2 SMIAPP_REG_MK_U8(0x0a4a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_3 SMIAPP_REG_MK_U8(0x0a4b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_4 SMIAPP_REG_MK_U8(0x0a4c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_5 SMIAPP_REG_MK_U8(0x0a4d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_6 SMIAPP_REG_MK_U8(0x0a4e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_7 SMIAPP_REG_MK_U8(0x0a4f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_8 SMIAPP_REG_MK_U8(0x0a50) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_9 SMIAPP_REG_MK_U8(0x0a51) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_10 SMIAPP_REG_MK_U8(0x0a52) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_11 SMIAPP_REG_MK_U8(0x0a53) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_12 SMIAPP_REG_MK_U8(0x0a54) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_13 SMIAPP_REG_MK_U8(0x0a55) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_14 SMIAPP_REG_MK_U8(0x0a56) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_15 SMIAPP_REG_MK_U8(0x0a57) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_16 SMIAPP_REG_MK_U8(0x0a58) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_17 SMIAPP_REG_MK_U8(0x0a59) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_18 SMIAPP_REG_MK_U8(0x0a5a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_19 SMIAPP_REG_MK_U8(0x0a5b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_20 SMIAPP_REG_MK_U8(0x0a5c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_21 SMIAPP_REG_MK_U8(0x0a5d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_22 SMIAPP_REG_MK_U8(0x0a5e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_23 SMIAPP_REG_MK_U8(0x0a5f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_24 SMIAPP_REG_MK_U8(0x0a60) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_25 SMIAPP_REG_MK_U8(0x0a61) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_26 SMIAPP_REG_MK_U8(0x0a62) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_27 SMIAPP_REG_MK_U8(0x0a63) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_28 SMIAPP_REG_MK_U8(0x0a64) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_29 SMIAPP_REG_MK_U8(0x0a65) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_30 SMIAPP_REG_MK_U8(0x0a66) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_31 SMIAPP_REG_MK_U8(0x0a67) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_32 SMIAPP_REG_MK_U8(0x0a68) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_33 SMIAPP_REG_MK_U8(0x0a69) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_34 SMIAPP_REG_MK_U8(0x0a6a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_35 SMIAPP_REG_MK_U8(0x0a6b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_36 SMIAPP_REG_MK_U8(0x0a6c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_37 SMIAPP_REG_MK_U8(0x0a6d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_38 SMIAPP_REG_MK_U8(0x0a6e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_39 SMIAPP_REG_MK_U8(0x0a6f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_40 SMIAPP_REG_MK_U8(0x0a70) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_41 SMIAPP_REG_MK_U8(0x0a71) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_42 SMIAPP_REG_MK_U8(0x0a72) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_43 SMIAPP_REG_MK_U8(0x0a73) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_44 SMIAPP_REG_MK_U8(0x0a74) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_45 SMIAPP_REG_MK_U8(0x0a75) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_46 SMIAPP_REG_MK_U8(0x0a76) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_47 SMIAPP_REG_MK_U8(0x0a77) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_48 SMIAPP_REG_MK_U8(0x0a78) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_49 SMIAPP_REG_MK_U8(0x0a79) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_50 SMIAPP_REG_MK_U8(0x0a7a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_51 SMIAPP_REG_MK_U8(0x0a7b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_52 SMIAPP_REG_MK_U8(0x0a7c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_53 SMIAPP_REG_MK_U8(0x0a7d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_54 SMIAPP_REG_MK_U8(0x0a7e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_55 SMIAPP_REG_MK_U8(0x0a7f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_56 SMIAPP_REG_MK_U8(0x0a80) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_57 SMIAPP_REG_MK_U8(0x0a81) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_58 SMIAPP_REG_MK_U8(0x0a82) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_59 SMIAPP_REG_MK_U8(0x0a83) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_60 SMIAPP_REG_MK_U8(0x0a84) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_61 SMIAPP_REG_MK_U8(0x0a85) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_62 SMIAPP_REG_MK_U8(0x0a86) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_63 SMIAPP_REG_MK_U8(0x0a87) +#define SMIAPP_REG_U8_SHADING_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b00) +#define SMIAPP_REG_U8_LUMINANCE_CORRECTION_LEVEL SMIAPP_REG_MK_U8(0x0b01) +#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_ENABLE SMIAPP_REG_MK_U8(0x0b02) +#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_WEIGHT SMIAPP_REG_MK_U8(0x0b03) +#define SMIAPP_REG_U8_BLACK_LEVEL_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b04) +#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b05) +#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b06) +#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b07) +#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b08) +#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b09) +#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b0a) +#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b0b) +#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b0c) +#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_WEIGHT SMIAPP_REG_MK_U8(0x0b0d) +#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b0e) +#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b0f) +#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b10) +#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b11) +#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b12) +#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b13) +#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b14) +#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b15) +#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b16) +#define SMIAPP_REG_U8_EDOF_MODE SMIAPP_REG_MK_U8(0x0b80) +#define SMIAPP_REG_U8_SHARPNESS SMIAPP_REG_MK_U8(0x0b83) +#define SMIAPP_REG_U8_DENOISING SMIAPP_REG_MK_U8(0x0b84) +#define SMIAPP_REG_U8_MODULE_SPECIFIC SMIAPP_REG_MK_U8(0x0b85) +#define SMIAPP_REG_U16_DEPTH_OF_FIELD SMIAPP_REG_MK_U16(0x0b86) +#define SMIAPP_REG_U16_FOCUS_DISTANCE SMIAPP_REG_MK_U16(0x0b88) +#define SMIAPP_REG_U8_ESTIMATION_MODE_CTRL SMIAPP_REG_MK_U8(0x0b8a) +#define SMIAPP_REG_U16_COLOUR_TEMPERATURE SMIAPP_REG_MK_U16(0x0b8c) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENR SMIAPP_REG_MK_U16(0x0b8e) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_RED SMIAPP_REG_MK_U16(0x0b90) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_BLUE SMIAPP_REG_MK_U16(0x0b92) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENB SMIAPP_REG_MK_U16(0x0b94) +#define SMIAPP_REG_U8_ESTIMATION_ZONE_MODE SMIAPP_REG_MK_U8(0x0bc0) +#define SMIAPP_REG_U16_FIXED_ZONE_WEIGHTING SMIAPP_REG_MK_U16(0x0bc2) +#define SMIAPP_REG_U16_CUSTOM_ZONE_X_START SMIAPP_REG_MK_U16(0x0bc4) +#define SMIAPP_REG_U16_CUSTOM_ZONE_Y_START SMIAPP_REG_MK_U16(0x0bc6) +#define SMIAPP_REG_U16_CUSTOM_ZONE_WIDTH SMIAPP_REG_MK_U16(0x0bc8) +#define SMIAPP_REG_U16_CUSTOM_ZONE_HEIGHT SMIAPP_REG_MK_U16(0x0bca) +#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL1 SMIAPP_REG_MK_U8(0x0c00) +#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL2 SMIAPP_REG_MK_U8(0x0c01) +#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_1 SMIAPP_REG_MK_U8(0x0c02) +#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_2 SMIAPP_REG_MK_U8(0x0c03) +#define SMIAPP_REG_U16_TRDY_CTRL SMIAPP_REG_MK_U16(0x0c04) +#define SMIAPP_REG_U16_TRDOUT_CTRL SMIAPP_REG_MK_U16(0x0c06) +#define SMIAPP_REG_U16_TSHUTTER_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c08) +#define SMIAPP_REG_U16_TSHUTTER_STROBE_WIDTH_CTRL SMIAPP_REG_MK_U16(0x0c0a) +#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c0c) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_CTRL SMIAPP_REG_MK_U16(0x0c0e) +#define SMIAPP_REG_U16_TGRST_INTERVAL_CTRL SMIAPP_REG_MK_U16(0x0c10) +#define SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT SMIAPP_REG_MK_U8(0x0c12) +#define SMIAPP_REG_U16_FLASH_STROBE_START_POINT SMIAPP_REG_MK_U16(0x0c14) +#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL SMIAPP_REG_MK_U16(0x0c16) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL SMIAPP_REG_MK_U16(0x0c18) +#define SMIAPP_REG_U8_FLASH_MODE_RS SMIAPP_REG_MK_U8(0x0c1a) +#define SMIAPP_REG_U8_FLASH_TRIGGER_RS SMIAPP_REG_MK_U8(0x0c1b) +#define SMIAPP_REG_U8_FLASH_STATUS SMIAPP_REG_MK_U8(0x0c1c) +#define SMIAPP_REG_U8_SA_STROBE_MODE SMIAPP_REG_MK_U8(0x0c1d) +#define SMIAPP_REG_U16_SA_STROBE_START_POINT SMIAPP_REG_MK_U16(0x0c1e) +#define SMIAPP_REG_U16_TSA_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c20) +#define SMIAPP_REG_U16_TSA_STROBE_WIDTH_CTRL SMIAPP_REG_MK_U16(0x0c22) +#define SMIAPP_REG_U8_SA_STROBE_TRIGGER SMIAPP_REG_MK_U8(0x0c24) +#define SMIAPP_REG_U8_SPECIAL_ACTUATOR_STATUS SMIAPP_REG_MK_U8(0x0c25) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_RS_CTRL SMIAPP_REG_MK_U16(0x0c26) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_RS_CTRL SMIAPP_REG_MK_U16(0x0c28) +#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_RS_CTRL SMIAPP_REG_MK_U8(0x0c2a) +#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_CTRL SMIAPP_REG_MK_U8(0x0c2b) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_CTRL SMIAPP_REG_MK_U16(0x0c2c) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_CTRL SMIAPP_REG_MK_U16(0x0c2e) +#define SMIAPP_REG_U8_LOW_LEVEL_CTRL SMIAPP_REG_MK_U8(0x0c80) +#define SMIAPP_REG_U16_MAIN_TRIGGER_REF_POINT SMIAPP_REG_MK_U16(0x0c82) +#define SMIAPP_REG_U16_MAIN_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c84) +#define SMIAPP_REG_U8_MAIN_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c86) +#define SMIAPP_REG_U16_PHASE1_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c88) +#define SMIAPP_REG_U8_PHASE1_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c8a) +#define SMIAPP_REG_U16_PHASE2_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c8c) +#define SMIAPP_REG_U8_PHASE2_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c8e) +#define SMIAPP_REG_U8_MECH_SHUTTER_CTRL SMIAPP_REG_MK_U8(0x0d00) +#define SMIAPP_REG_U8_OPERATION_MODE SMIAPP_REG_MK_U8(0x0d01) +#define SMIAPP_REG_U8_ACT_STATE1 SMIAPP_REG_MK_U8(0x0d02) +#define SMIAPP_REG_U8_ACT_STATE2 SMIAPP_REG_MK_U8(0x0d03) +#define SMIAPP_REG_U16_FOCUS_CHANGE SMIAPP_REG_MK_U16(0x0d80) +#define SMIAPP_REG_U16_FOCUS_CHANGE_CONTROL SMIAPP_REG_MK_U16(0x0d82) +#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE1 SMIAPP_REG_MK_U16(0x0d84) +#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE2 SMIAPP_REG_MK_U16(0x0d86) +#define SMIAPP_REG_U8_STROBE_COUNT_PHASE1 SMIAPP_REG_MK_U8(0x0d88) +#define SMIAPP_REG_U8_STROBE_COUNT_PHASE2 SMIAPP_REG_MK_U8(0x0d89) +#define SMIAPP_REG_U8_POSITION SMIAPP_REG_MK_U8(0x0d8a) +#define SMIAPP_REG_U8_BRACKETING_LUT_CONTROL SMIAPP_REG_MK_U8(0x0e00) +#define SMIAPP_REG_U8_BRACKETING_LUT_MODE SMIAPP_REG_MK_U8(0x0e01) +#define SMIAPP_REG_U8_BRACKETING_LUT_ENTRY_CONTROL SMIAPP_REG_MK_U8(0x0e02) +#define SMIAPP_REG_U8_LUT_PARAMETERS_START SMIAPP_REG_MK_U8(0x0e10) +#define SMIAPP_REG_U8_LUT_PARAMETERS_END SMIAPP_REG_MK_U8(0x0eff) +#define SMIAPP_REG_U16_INTEGRATION_TIME_CAPABILITY SMIAPP_REG_MK_U16(0x1000) +#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MIN SMIAPP_REG_MK_U16(0x1004) +#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MAX_MARGIN SMIAPP_REG_MK_U16(0x1006) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN SMIAPP_REG_MK_U16(0x1008) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN SMIAPP_REG_MK_U16(0x100a) +#define SMIAPP_REG_U16_DIGITAL_GAIN_CAPABILITY SMIAPP_REG_MK_U16(0x1080) +#define SMIAPP_REG_U16_DIGITAL_GAIN_MIN SMIAPP_REG_MK_U16(0x1084) +#define SMIAPP_REG_U16_DIGITAL_GAIN_MAX SMIAPP_REG_MK_U16(0x1086) +#define SMIAPP_REG_U16_DIGITAL_GAIN_STEP_SIZE SMIAPP_REG_MK_U16(0x1088) +#define SMIAPP_REG_F32_MIN_EXT_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1100) +#define SMIAPP_REG_F32_MAX_EXT_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1104) +#define SMIAPP_REG_U16_MIN_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x1108) +#define SMIAPP_REG_U16_MAX_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x110a) +#define SMIAPP_REG_F32_MIN_PLL_IP_FREQ_HZ SMIAPP_REG_MK_F32(0x110c) +#define SMIAPP_REG_F32_MAX_PLL_IP_FREQ_HZ SMIAPP_REG_MK_F32(0x1110) +#define SMIAPP_REG_U16_MIN_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x1114) +#define SMIAPP_REG_U16_MAX_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x1116) +#define SMIAPP_REG_F32_MIN_PLL_OP_FREQ_HZ SMIAPP_REG_MK_F32(0x1118) +#define SMIAPP_REG_F32_MAX_PLL_OP_FREQ_HZ SMIAPP_REG_MK_F32(0x111c) +#define SMIAPP_REG_U16_MIN_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1120) +#define SMIAPP_REG_U16_MAX_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1122) +#define SMIAPP_REG_F32_MIN_VT_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1124) +#define SMIAPP_REG_F32_MAX_VT_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1128) +#define SMIAPP_REG_F32_MIN_VT_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x112c) +#define SMIAPP_REG_F32_MAX_VT_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1130) +#define SMIAPP_REG_U16_MIN_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x1134) +#define SMIAPP_REG_U16_MAX_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x1136) +#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x1140) +#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x1142) +#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x1144) +#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x1146) +#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK SMIAPP_REG_MK_U16(0x1148) +#define SMIAPP_REG_U16_MIN_FRAME_BLANKING_LINES SMIAPP_REG_MK_U16(0x114a) +#define SMIAPP_REG_U8_MIN_LINE_LENGTH_PCK_STEP_SIZE SMIAPP_REG_MK_U8(0x114c) +#define SMIAPP_REG_U16_MIN_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1160) +#define SMIAPP_REG_U16_MAX_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1162) +#define SMIAPP_REG_F32_MIN_OP_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1164) +#define SMIAPP_REG_F32_MAX_OP_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1168) +#define SMIAPP_REG_U16_MIN_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x116c) +#define SMIAPP_REG_U16_MAX_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x116e) +#define SMIAPP_REG_F32_MIN_OP_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1170) +#define SMIAPP_REG_F32_MAX_OP_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1174) +#define SMIAPP_REG_U16_X_ADDR_MIN SMIAPP_REG_MK_U16(0x1180) +#define SMIAPP_REG_U16_Y_ADDR_MIN SMIAPP_REG_MK_U16(0x1182) +#define SMIAPP_REG_U16_X_ADDR_MAX SMIAPP_REG_MK_U16(0x1184) +#define SMIAPP_REG_U16_Y_ADDR_MAX SMIAPP_REG_MK_U16(0x1186) +#define SMIAPP_REG_U16_MIN_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x1188) +#define SMIAPP_REG_U16_MIN_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118a) +#define SMIAPP_REG_U16_MAX_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118c) +#define SMIAPP_REG_U16_MAX_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118e) +#define SMIAPP_REG_U16_MIN_EVEN_INC SMIAPP_REG_MK_U16(0x11c0) +#define SMIAPP_REG_U16_MAX_EVEN_INC SMIAPP_REG_MK_U16(0x11c2) +#define SMIAPP_REG_U16_MIN_ODD_INC SMIAPP_REG_MK_U16(0x11c4) +#define SMIAPP_REG_U16_MAX_ODD_INC SMIAPP_REG_MK_U16(0x11c6) +#define SMIAPP_REG_U16_SCALING_CAPABILITY SMIAPP_REG_MK_U16(0x1200) +#define SMIAPP_REG_U16_SCALER_M_MIN SMIAPP_REG_MK_U16(0x1204) +#define SMIAPP_REG_U16_SCALER_M_MAX SMIAPP_REG_MK_U16(0x1206) +#define SMIAPP_REG_U16_SCALER_N_MIN SMIAPP_REG_MK_U16(0x1208) +#define SMIAPP_REG_U16_SCALER_N_MAX SMIAPP_REG_MK_U16(0x120a) +#define SMIAPP_REG_U16_SPATIAL_SAMPLING_CAPABILITY SMIAPP_REG_MK_U16(0x120c) +#define SMIAPP_REG_U8_DIGITAL_CROP_CAPABILITY SMIAPP_REG_MK_U8(0x120e) +#define SMIAPP_REG_U16_COMPRESSION_CAPABILITY SMIAPP_REG_MK_U16(0x1300) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINRED SMIAPP_REG_MK_U16(0x1400) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINRED SMIAPP_REG_MK_U16(0x1402) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINRED SMIAPP_REG_MK_U16(0x1404) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINGREEN SMIAPP_REG_MK_U16(0x1406) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINGREEN SMIAPP_REG_MK_U16(0x1408) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINGREEN SMIAPP_REG_MK_U16(0x140a) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINBLUE SMIAPP_REG_MK_U16(0x140c) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINBLUE SMIAPP_REG_MK_U16(0x140e) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINBLUE SMIAPP_REG_MK_U16(0x1410) +#define SMIAPP_REG_U16_FIFO_SIZE_PIXELS SMIAPP_REG_MK_U16(0x1500) +#define SMIAPP_REG_U8_FIFO_SUPPORT_CAPABILITY SMIAPP_REG_MK_U8(0x1502) +#define SMIAPP_REG_U8_DPHY_CTRL_CAPABILITY SMIAPP_REG_MK_U8(0x1600) +#define SMIAPP_REG_U8_CSI_LANE_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1601) +#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1602) +#define SMIAPP_REG_U8_FAST_STANDBY_CAPABILITY SMIAPP_REG_MK_U8(0x1603) +#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL_CAPABILITY SMIAPP_REG_MK_U8(0x1604) +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1608) +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x160c) +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1610) +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1614) +#define SMIAPP_REG_U8_TEMP_SENSOR_CAPABILITY SMIAPP_REG_MK_U8(0x1618) +#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES_BIN SMIAPP_REG_MK_U16(0x1700) +#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES_BIN SMIAPP_REG_MK_U16(0x1702) +#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK_BIN SMIAPP_REG_MK_U16(0x1704) +#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK_BIN SMIAPP_REG_MK_U16(0x1706) +#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK_BIN SMIAPP_REG_MK_U16(0x1708) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN_BIN SMIAPP_REG_MK_U16(0x170a) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN SMIAPP_REG_MK_U16(0x170c) +#define SMIAPP_REG_U8_BINNING_CAPABILITY SMIAPP_REG_MK_U8(0x1710) +#define SMIAPP_REG_U8_BINNING_WEIGHTING_CAPABILITY SMIAPP_REG_MK_U8(0x1711) +#define SMIAPP_REG_U8_BINNING_SUBTYPES SMIAPP_REG_MK_U8(0x1712) +#define SMIAPP_REG_U8_BINNING_TYPE_n(n) SMIAPP_REG_MK_U8(0x1713 + (n)) /* 1 <= n <= 237 */ +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_CAPABILITY SMIAPP_REG_MK_U8(0x1800) +#define SMIAPP_REG_U8_SHADING_CORRECTION_CAPABILITY SMIAPP_REG_MK_U8(0x1900) +#define SMIAPP_REG_U8_GREEN_IMBALANCE_CAPABILITY SMIAPP_REG_MK_U8(0x1901) +#define SMIAPP_REG_U8_BLACK_LEVEL_CAPABILITY SMIAPP_REG_MK_U8(0x1902) +#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_CAPABILITY SMIAPP_REG_MK_U8(0x1903) +#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY SMIAPP_REG_MK_U16(0x1904) +#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY_2 SMIAPP_REG_MK_U16(0x1906) +#define SMIAPP_REG_U8_EDOF_CAPABILITY SMIAPP_REG_MK_U8(0x1980) +#define SMIAPP_REG_U8_ESTIMATION_FRAMES SMIAPP_REG_MK_U8(0x1981) +#define SMIAPP_REG_U8_SUPPORTS_SHARPNESS_ADJ SMIAPP_REG_MK_U8(0x1982) +#define SMIAPP_REG_U8_SUPPORTS_DENOISING_ADJ SMIAPP_REG_MK_U8(0x1983) +#define SMIAPP_REG_U8_SUPPORTS_MODULE_SPECIFIC_ADJ SMIAPP_REG_MK_U8(0x1984) +#define SMIAPP_REG_U8_SUPPORTS_DEPTH_OF_FIELD_ADJ SMIAPP_REG_MK_U8(0x1985) +#define SMIAPP_REG_U8_SUPPORTS_FOCUS_DISTANCE_ADJ SMIAPP_REG_MK_U8(0x1986) +#define SMIAPP_REG_U8_COLOUR_FEEDBACK_CAPABILITY SMIAPP_REG_MK_U8(0x1987) +#define SMIAPP_REG_U8_EDOF_SUPPORT_AB_NXM SMIAPP_REG_MK_U8(0x1988) +#define SMIAPP_REG_U8_ESTIMATION_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x19c0) +#define SMIAPP_REG_U8_ESTIMATION_ZONE_CAPABILITY SMIAPP_REG_MK_U8(0x19c1) +#define SMIAPP_REG_U16_EST_DEPTH_OF_FIELD SMIAPP_REG_MK_U16(0x19c2) +#define SMIAPP_REG_U16_EST_FOCUS_DISTANCE SMIAPP_REG_MK_U16(0x19c4) +#define SMIAPP_REG_U16_CAPABILITY_TRDY_MIN SMIAPP_REG_MK_U16(0x1a00) +#define SMIAPP_REG_U8_FLASH_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1a02) +#define SMIAPP_REG_U16_MECH_SHUT_AND_ACT_START_ADDR SMIAPP_REG_MK_U16(0x1b02) +#define SMIAPP_REG_U8_ACTUATOR_CAPABILITY SMIAPP_REG_MK_U8(0x1b04) +#define SMIAPP_REG_U16_ACTUATOR_TYPE SMIAPP_REG_MK_U16(0x1b40) +#define SMIAPP_REG_U8_AF_DEVICE_ADDRESS SMIAPP_REG_MK_U8(0x1b42) +#define SMIAPP_REG_U16_FOCUS_CHANGE_ADDRESS SMIAPP_REG_MK_U16(0x1b44) +#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_1 SMIAPP_REG_MK_U8(0x1c00) +#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_2 SMIAPP_REG_MK_U8(0x1c01) +#define SMIAPP_REG_U8_BRACKETING_LUT_SIZE SMIAPP_REG_MK_U8(0x1c02) diff --git a/drivers/media/i2c/smiapp/smiapp-reg.h b/drivers/media/i2c/smiapp/smiapp-reg.h new file mode 100644 index 00000000000..54568ca2fe6 --- /dev/null +++ b/drivers/media/i2c/smiapp/smiapp-reg.h @@ -0,0 +1,122 @@ +/* + * drivers/media/i2c/smiapp/smiapp-reg.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __SMIAPP_REG_H_ +#define __SMIAPP_REG_H_ + +#include "smiapp-reg-defs.h" + +/* Bits for above register */ +#define SMIAPP_IMAGE_ORIENTATION_HFLIP (1 << 0) +#define SMIAPP_IMAGE_ORIENTATION_VFLIP (1 << 1) + +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN (1 << 0) +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN (0 << 1) +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_WR_EN (1 << 1) +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_ERR_CLEAR (1 << 2) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY (1 << 0) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_WR_READY (1 << 1) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EDATA (1 << 2) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EUSAGE (1 << 3) + +#define SMIAPP_SOFTWARE_RESET (1 << 0) + +#define SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE (1 << 0) +#define SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE (1 << 1) + +#define SMIAPP_DPHY_CTRL_AUTOMATIC 0 +/* DPHY control based on REQUESTED_LINK_BIT_RATE_MBPS */ +#define SMIAPP_DPHY_CTRL_UI 1 +#define SMIAPP_DPHY_CTRL_REGISTER 2 + +#define SMIAPP_COMPRESSION_MODE_SIMPLE_PREDICTOR 1 +#define SMIAPP_COMPRESSION_MODE_ADVANCED_PREDICTOR 2 + +#define SMIAPP_MODE_SELECT_SOFTWARE_STANDBY 0 +#define SMIAPP_MODE_SELECT_STREAMING 1 + +#define SMIAPP_SCALING_MODE_NONE 0 +#define SMIAPP_SCALING_MODE_HORIZONTAL 1 +#define SMIAPP_SCALING_MODE_BOTH 2 + +#define SMIAPP_SCALING_CAPABILITY_NONE 0 +#define SMIAPP_SCALING_CAPABILITY_HORIZONTAL 1 +#define SMIAPP_SCALING_CAPABILITY_BOTH 2 /* horizontal/both */ + +/* digital crop right before scaler */ +#define SMIAPP_DIGITAL_CROP_CAPABILITY_NONE 0 +#define SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP 1 + +#define SMIAPP_BINNING_CAPABILITY_NO 0 +#define SMIAPP_BINNING_CAPABILITY_YES 1 + +/* Maximum number of binning subtypes */ +#define SMIAPP_BINNING_SUBTYPES 253 + +#define SMIAPP_PIXEL_ORDER_GRBG 0 +#define SMIAPP_PIXEL_ORDER_RGGB 1 +#define SMIAPP_PIXEL_ORDER_BGGR 2 +#define SMIAPP_PIXEL_ORDER_GBRG 3 + +#define SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL 1 +#define SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED 2 +#define SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL_N 8 +#define SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED_N 16 + +#define SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE 0x01 +#define SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE 0x02 +#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NROWS_MASK 0x0f +#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_MASK 0xf0 +#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_SHIFT 4 + +#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_MASK 0xf000 +#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_SHIFT 12 +#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELS_MASK 0x0fff + +#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_MASK 0xf0000000 +#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_SHIFT 28 +#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELS_MASK 0x0000ffff + +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED 1 +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DUMMY 2 +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_BLACK 3 +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DARK 4 +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE 5 + +#define SMIAPP_FAST_STANDBY_CTRL_COMPLETE_FRAMES 0 +#define SMIAPP_FAST_STANDBY_CTRL_IMMEDIATE 1 + +/* Scaling N factor */ +#define SMIAPP_SCALE_N 16 + +/* Image statistics registers */ +/* Registers 0x2000 to 0x2fff are reserved for future + * use for statistics features. + */ + +/* Manufacturer Specific Registers: 0x3000 to 0x3fff + * The manufacturer specifies these as a black box. + */ + +#endif /* __SMIAPP_REG_H_ */ diff --git a/drivers/media/i2c/smiapp/smiapp-regs.c b/drivers/media/i2c/smiapp/smiapp-regs.c new file mode 100644 index 00000000000..70e0d8db013 --- /dev/null +++ b/drivers/media/i2c/smiapp/smiapp-regs.c @@ -0,0 +1,273 @@ +/* + * drivers/media/i2c/smiapp/smiapp-regs.c + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include + +#include "smiapp.h" +#include "smiapp-regs.h" + +static uint32_t float_to_u32_mul_1000000(struct i2c_client *client, + uint32_t phloat) +{ + int32_t exp; + uint64_t man; + + if (phloat >= 0x80000000) { + dev_err(&client->dev, "this is a negative number\n"); + return 0; + } + + if (phloat == 0x7f800000) + return ~0; /* Inf. */ + + if ((phloat & 0x7f800000) == 0x7f800000) { + dev_err(&client->dev, "NaN or other special number\n"); + return 0; + } + + /* Valid cases begin here */ + if (phloat == 0) + return 0; /* Valid zero */ + + if (phloat > 0x4f800000) + return ~0; /* larger than 4294967295 */ + + /* + * Unbias exponent (note how phloat is now guaranteed to + * have 0 in the high bit) + */ + exp = ((int32_t)phloat >> 23) - 127; + + /* Extract mantissa, add missing '1' bit and it's in MHz */ + man = ((phloat & 0x7fffff) | 0x800000) * 1000000ULL; + + if (exp < 0) + man >>= -exp; + else + man <<= exp; + + man >>= 23; /* Remove mantissa bias */ + + return man & 0xffffffff; +} + + +/* + * Read a 8/16/32-bit i2c register. The value is returned in 'val'. + * Returns zero if successful, or non-zero otherwise. + */ +static int ____smiapp_read(struct smiapp_sensor *sensor, u16 reg, + u16 len, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct i2c_msg msg; + unsigned char data[4]; + u16 offset = reg; + int r; + + msg.addr = client->addr; + msg.flags = 0; + msg.len = 2; + msg.buf = data; + + /* high byte goes out first */ + data[0] = (u8) (offset >> 8); + data[1] = (u8) offset; + r = i2c_transfer(client->adapter, &msg, 1); + if (r != 1) { + if (r >= 0) + r = -EBUSY; + goto err; + } + + msg.len = len; + msg.flags = I2C_M_RD; + r = i2c_transfer(client->adapter, &msg, 1); + if (r != 1) { + if (r >= 0) + r = -EBUSY; + goto err; + } + + *val = 0; + /* high byte comes first */ + switch (len) { + case SMIA_REG_32BIT: + *val = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + + data[3]; + break; + case SMIA_REG_16BIT: + *val = (data[0] << 8) + data[1]; + break; + case SMIA_REG_8BIT: + *val = data[0]; + break; + default: + BUG(); + } + + return 0; + +err: + dev_err(&client->dev, "read from offset 0x%x error %d\n", offset, r); + + return r; +} + +/* Read a register using 8-bit access only. */ +static int ____smiapp_read_8only(struct smiapp_sensor *sensor, u16 reg, + u16 len, u32 *val) +{ + unsigned int i; + int rval; + + *val = 0; + + for (i = 0; i < len; i++) { + u32 val8; + + rval = ____smiapp_read(sensor, reg + i, 1, &val8); + if (rval < 0) + return rval; + *val |= val8 << ((len - i - 1) << 3); + } + + return 0; +} + +/* + * Read a 8/16/32-bit i2c register. The value is returned in 'val'. + * Returns zero if successful, or non-zero otherwise. + */ +static int __smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val, + bool only8) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int len = (u8)(reg >> 16); + int rval; + + if (len != SMIA_REG_8BIT && len != SMIA_REG_16BIT + && len != SMIA_REG_32BIT) + return -EINVAL; + + if (smiapp_quirk_reg(sensor, reg, val)) + goto found_quirk; + + if (len == SMIA_REG_8BIT && !only8) + rval = ____smiapp_read(sensor, (u16)reg, len, val); + else + rval = ____smiapp_read_8only(sensor, (u16)reg, len, val); + if (rval < 0) + return rval; + +found_quirk: + if (reg & SMIA_REG_FLAG_FLOAT) + *val = float_to_u32_mul_1000000(client, *val); + + return 0; +} + +int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val) +{ + return __smiapp_read( + sensor, reg, val, + smiapp_needs_quirk(sensor, + SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY)); +} + +int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val) +{ + return __smiapp_read(sensor, reg, val, true); +} + +/* + * Write to a 8/16-bit register. + * Returns zero if successful, or non-zero otherwise. + */ +int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct i2c_msg msg; + unsigned char data[6]; + unsigned int retries; + unsigned int flags = reg >> 24; + unsigned int len = (u8)(reg >> 16); + u16 offset = reg; + int r; + + if ((len != SMIA_REG_8BIT && len != SMIA_REG_16BIT && + len != SMIA_REG_32BIT) || flags) + return -EINVAL; + + msg.addr = client->addr; + msg.flags = 0; /* Write */ + msg.len = 2 + len; + msg.buf = data; + + /* high byte goes out first */ + data[0] = (u8) (reg >> 8); + data[1] = (u8) (reg & 0xff); + + switch (len) { + case SMIA_REG_8BIT: + data[2] = val; + break; + case SMIA_REG_16BIT: + data[2] = val >> 8; + data[3] = val; + break; + case SMIA_REG_32BIT: + data[2] = val >> 24; + data[3] = val >> 16; + data[4] = val >> 8; + data[5] = val; + break; + default: + BUG(); + } + + for (retries = 0; retries < 5; retries++) { + /* + * Due to unknown reason sensor stops responding. This + * loop is a temporaty solution until the root cause + * is found. + */ + r = i2c_transfer(client->adapter, &msg, 1); + if (r == 1) { + if (retries) + dev_err(&client->dev, + "sensor i2c stall encountered. " + "retries: %d\n", retries); + return 0; + } + + usleep_range(2000, 2000); + } + + dev_err(&client->dev, + "wrote 0x%x to offset 0x%x error %d\n", val, offset, r); + + return r; +} diff --git a/drivers/media/i2c/smiapp/smiapp-regs.h b/drivers/media/i2c/smiapp/smiapp-regs.h new file mode 100644 index 00000000000..7f9013b4797 --- /dev/null +++ b/drivers/media/i2c/smiapp/smiapp-regs.h @@ -0,0 +1,49 @@ +/* + * include/media/smiapp/smiapp-regs.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef SMIAPP_REGS_H +#define SMIAPP_REGS_H + +#include +#include + +/* Use upper 8 bits of the type field for flags */ +#define SMIA_REG_FLAG_FLOAT (1 << 24) + +#define SMIA_REG_8BIT 1 +#define SMIA_REG_16BIT 2 +#define SMIA_REG_32BIT 4 +struct smia_reg { + u16 type; + u16 reg; /* 16-bit offset */ + u32 val; /* 8/16/32-bit value */ +}; + +struct smiapp_sensor; + +int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val); +int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val); +int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val); + +#endif diff --git a/drivers/media/i2c/smiapp/smiapp.h b/drivers/media/i2c/smiapp/smiapp.h new file mode 100644 index 00000000000..4182a695ab5 --- /dev/null +++ b/drivers/media/i2c/smiapp/smiapp.h @@ -0,0 +1,252 @@ +/* + * drivers/media/i2c/smiapp/smiapp.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2010--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __SMIAPP_PRIV_H_ +#define __SMIAPP_PRIV_H_ + +#include +#include +#include +#include + +#include "smiapp-pll.h" +#include "smiapp-reg.h" +#include "smiapp-regs.h" +#include "smiapp-quirk.h" + +/* + * Standard SMIA++ constants + */ +#define SMIA_VERSION_1 10 +#define SMIAPP_VERSION_0_8 8 /* Draft 0.8 */ +#define SMIAPP_VERSION_0_9 9 /* Draft 0.9 */ +#define SMIAPP_VERSION_1 10 + +#define SMIAPP_PROFILE_0 0 +#define SMIAPP_PROFILE_1 1 +#define SMIAPP_PROFILE_2 2 + +#define SMIAPP_NVM_PAGE_SIZE 64 /* bytes */ + +#define SMIAPP_RESET_DELAY_CLOCKS 2400 +#define SMIAPP_RESET_DELAY(clk) \ + (1000 + (SMIAPP_RESET_DELAY_CLOCKS * 1000 \ + + (clk) / 1000 - 1) / ((clk) / 1000)) + +#include "smiapp-limits.h" + +struct smiapp_quirk; + +#define SMIAPP_MODULE_IDENT_FLAG_REV_LE (1 << 0) + +struct smiapp_module_ident { + u8 manufacturer_id; + u16 model_id; + u8 revision_number_major; + + u8 flags; + + char *name; + const struct smiapp_quirk *quirk; +}; + +struct smiapp_module_info { + u32 manufacturer_id; + u32 model_id; + u32 revision_number_major; + u32 revision_number_minor; + + u32 module_year; + u32 module_month; + u32 module_day; + + u32 sensor_manufacturer_id; + u32 sensor_model_id; + u32 sensor_revision_number; + u32 sensor_firmware_version; + + u32 smia_version; + u32 smiapp_version; + + u32 smiapp_profile; + + char *name; + const struct smiapp_quirk *quirk; +}; + +#define SMIAPP_IDENT_FQ(manufacturer, model, rev, fl, _name, _quirk) \ + { .manufacturer_id = manufacturer, \ + .model_id = model, \ + .revision_number_major = rev, \ + .flags = fl, \ + .name = _name, \ + .quirk = _quirk, } + +#define SMIAPP_IDENT_LQ(manufacturer, model, rev, _name, _quirk) \ + { .manufacturer_id = manufacturer, \ + .model_id = model, \ + .revision_number_major = rev, \ + .flags = SMIAPP_MODULE_IDENT_FLAG_REV_LE, \ + .name = _name, \ + .quirk = _quirk, } + +#define SMIAPP_IDENT_L(manufacturer, model, rev, _name) \ + { .manufacturer_id = manufacturer, \ + .model_id = model, \ + .revision_number_major = rev, \ + .flags = SMIAPP_MODULE_IDENT_FLAG_REV_LE, \ + .name = _name, } + +#define SMIAPP_IDENT_Q(manufacturer, model, rev, _name, _quirk) \ + { .manufacturer_id = manufacturer, \ + .model_id = model, \ + .revision_number_major = rev, \ + .flags = 0, \ + .name = _name, \ + .quirk = _quirk, } + +#define SMIAPP_IDENT(manufacturer, model, rev, _name) \ + { .manufacturer_id = manufacturer, \ + .model_id = model, \ + .revision_number_major = rev, \ + .flags = 0, \ + .name = _name, } + +struct smiapp_reg_limits { + u32 addr; + char *what; +}; + +extern struct smiapp_reg_limits smiapp_reg_limits[]; + +struct smiapp_csi_data_format { + u32 code; + u8 width; + u8 compressed; + u8 pixel_order; +}; + +#define SMIAPP_SUBDEVS 3 + +#define SMIAPP_PA_PAD_SRC 0 +#define SMIAPP_PAD_SINK 0 +#define SMIAPP_PAD_SRC 1 +#define SMIAPP_PADS 2 + +struct smiapp_binning_subtype { + u8 horizontal:4; + u8 vertical:4; +} __packed; + +struct smiapp_subdev { + struct v4l2_subdev sd; + struct media_pad pads[2]; + struct v4l2_rect sink_fmt; + struct v4l2_rect crop[2]; + struct v4l2_rect compose; /* compose on sink */ + unsigned short sink_pad; + unsigned short source_pad; + int npads; + struct smiapp_sensor *sensor; + struct v4l2_ctrl_handler ctrl_handler; +}; + +/* + * struct smiapp_sensor - Main device structure + */ +struct smiapp_sensor { + /* + * "mutex" is used to serialise access to all fields here + * except v4l2_ctrls at the end of the struct. "mutex" is also + * used to serialise access to file handle specific + * information. The exception to this rule is the power_mutex + * below. + */ + struct mutex mutex; + /* + * power_mutex is used to serialise power management related + * activities. Acquiring "mutex" at that time isn't necessary + * since there are no other users anyway. + */ + struct mutex power_mutex; + struct smiapp_subdev ssds[SMIAPP_SUBDEVS]; + u32 ssds_used; + struct smiapp_subdev *src; + struct smiapp_subdev *binner; + struct smiapp_subdev *scaler; + struct smiapp_subdev *pixel_array; + struct smiapp_platform_data *platform_data; + struct regulator *vana; + struct clk *ext_clk; + u32 limits[SMIAPP_LIMIT_LAST]; + u8 nbinning_subtypes; + struct smiapp_binning_subtype binning_subtypes[SMIAPP_BINNING_SUBTYPES]; + u32 mbus_frame_fmts; + const struct smiapp_csi_data_format *csi_format; + const struct smiapp_csi_data_format *internal_csi_format; + u32 default_mbus_frame_fmts; + int default_pixel_order; + + u8 binning_horizontal; + u8 binning_vertical; + + u8 scale_m; + u8 scaling_mode; + + u8 hvflip_inv_mask; /* H/VFLIP inversion due to sensor orientation */ + u8 flash_capability; + u8 frame_skip; + + int power_count; + + bool streaming; + bool dev_init_done; + + u8 *nvm; /* nvm memory buffer */ + unsigned int nvm_size; /* bytes */ + + struct smiapp_module_info minfo; + + struct smiapp_pll pll; + + /* Pixel array controls */ + struct v4l2_ctrl *analog_gain; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *pixel_rate_parray; + /* src controls */ + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate_csi; +}; + +#define to_smiapp_subdev(_sd) \ + container_of(_sd, struct smiapp_subdev, sd) + +#define to_smiapp_sensor(_sd) \ + (to_smiapp_subdev(_sd)->sensor) + +#endif /* __SMIAPP_PRIV_H_ */ diff --git a/drivers/media/i2c/sr030pc30.c b/drivers/media/i2c/sr030pc30.c new file mode 100644 index 00000000000..e9d95bda2ab --- /dev/null +++ b/drivers/media/i2c/sr030pc30.c @@ -0,0 +1,871 @@ +/* + * Driver for SiliconFile SR030PC30 VGA (1/10-Inch) Image Sensor with ISP + * + * Copyright (C) 2010 Samsung Electronics Co., Ltd + * Author: Sylwester Nawrocki, s.nawrocki@samsung.com + * + * Based on original driver authored by Dongsoo Nathaniel Kim + * and HeungJun Kim . + * + * Based on mt9v011 Micron Digital Image Sensor driver + * Copyright (c) 2009 Mauro Carvalho Chehab (mchehab@redhat.com) + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static int debug; +module_param(debug, int, 0644); + +#define MODULE_NAME "SR030PC30" + +/* + * Register offsets within a page + * b15..b8 - page id, b7..b0 - register address + */ +#define POWER_CTRL_REG 0x0001 +#define PAGEMODE_REG 0x03 +#define DEVICE_ID_REG 0x0004 +#define NOON010PC30_ID 0x86 +#define SR030PC30_ID 0x8C +#define VDO_CTL1_REG 0x0010 +#define SUBSAMPL_NONE_VGA 0 +#define SUBSAMPL_QVGA 0x10 +#define SUBSAMPL_QQVGA 0x20 +#define VDO_CTL2_REG 0x0011 +#define SYNC_CTL_REG 0x0012 +#define WIN_ROWH_REG 0x0020 +#define WIN_ROWL_REG 0x0021 +#define WIN_COLH_REG 0x0022 +#define WIN_COLL_REG 0x0023 +#define WIN_HEIGHTH_REG 0x0024 +#define WIN_HEIGHTL_REG 0x0025 +#define WIN_WIDTHH_REG 0x0026 +#define WIN_WIDTHL_REG 0x0027 +#define HBLANKH_REG 0x0040 +#define HBLANKL_REG 0x0041 +#define VSYNCH_REG 0x0042 +#define VSYNCL_REG 0x0043 +/* page 10 */ +#define ISP_CTL_REG(n) (0x1010 + (n)) +#define YOFS_REG 0x1040 +#define DARK_YOFS_REG 0x1041 +#define AG_ABRTH_REG 0x1050 +#define SAT_CTL_REG 0x1060 +#define BSAT_REG 0x1061 +#define RSAT_REG 0x1062 +#define AG_SAT_TH_REG 0x1063 +/* page 11 */ +#define ZLPF_CTRL_REG 0x1110 +#define ZLPF_CTRL2_REG 0x1112 +#define ZLPF_AGH_THR_REG 0x1121 +#define ZLPF_THR_REG 0x1160 +#define ZLPF_DYN_THR_REG 0x1160 +/* page 12 */ +#define YCLPF_CTL1_REG 0x1240 +#define YCLPF_CTL2_REG 0x1241 +#define YCLPF_THR_REG 0x1250 +#define BLPF_CTL_REG 0x1270 +#define BLPF_THR1_REG 0x1274 +#define BLPF_THR2_REG 0x1275 +/* page 14 - Lens Shading Compensation */ +#define LENS_CTRL_REG 0x1410 +#define LENS_XCEN_REG 0x1420 +#define LENS_YCEN_REG 0x1421 +#define LENS_R_COMP_REG 0x1422 +#define LENS_G_COMP_REG 0x1423 +#define LENS_B_COMP_REG 0x1424 +/* page 15 - Color correction */ +#define CMC_CTL_REG 0x1510 +#define CMC_OFSGH_REG 0x1514 +#define CMC_OFSGL_REG 0x1516 +#define CMC_SIGN_REG 0x1517 +/* Color correction coefficients */ +#define CMC_COEF_REG(n) (0x1530 + (n)) +/* Color correction offset coefficients */ +#define CMC_OFS_REG(n) (0x1540 + (n)) +/* page 16 - Gamma correction */ +#define GMA_CTL_REG 0x1610 +/* Gamma correction coefficients 0.14 */ +#define GMA_COEF_REG(n) (0x1630 + (n)) +/* page 20 - Auto Exposure */ +#define AE_CTL1_REG 0x2010 +#define AE_CTL2_REG 0x2011 +#define AE_FRM_CTL_REG 0x2020 +#define AE_FINE_CTL_REG(n) (0x2028 + (n)) +#define EXP_TIMEH_REG 0x2083 +#define EXP_TIMEM_REG 0x2084 +#define EXP_TIMEL_REG 0x2085 +#define EXP_MMINH_REG 0x2086 +#define EXP_MMINL_REG 0x2087 +#define EXP_MMAXH_REG 0x2088 +#define EXP_MMAXM_REG 0x2089 +#define EXP_MMAXL_REG 0x208A +/* page 22 - Auto White Balance */ +#define AWB_CTL1_REG 0x2210 +#define AWB_ENABLE 0x80 +#define AWB_CTL2_REG 0x2211 +#define MWB_ENABLE 0x01 +/* RGB gain control (manual WB) when AWB_CTL1[7]=0 */ +#define AWB_RGAIN_REG 0x2280 +#define AWB_GGAIN_REG 0x2281 +#define AWB_BGAIN_REG 0x2282 +#define AWB_RMAX_REG 0x2283 +#define AWB_RMIN_REG 0x2284 +#define AWB_BMAX_REG 0x2285 +#define AWB_BMIN_REG 0x2286 +/* R, B gain range in bright light conditions */ +#define AWB_RMAXB_REG 0x2287 +#define AWB_RMINB_REG 0x2288 +#define AWB_BMAXB_REG 0x2289 +#define AWB_BMINB_REG 0x228A +/* manual white balance, when AWB_CTL2[0]=1 */ +#define MWB_RGAIN_REG 0x22B2 +#define MWB_BGAIN_REG 0x22B3 +/* the token to mark an array end */ +#define REG_TERM 0xFFFF + +/* Minimum and maximum exposure time in ms */ +#define EXPOS_MIN_MS 1 +#define EXPOS_MAX_MS 125 + +struct sr030pc30_info { + struct v4l2_subdev sd; + const struct sr030pc30_platform_data *pdata; + const struct sr030pc30_format *curr_fmt; + const struct sr030pc30_frmsize *curr_win; + unsigned int auto_wb:1; + unsigned int auto_exp:1; + unsigned int hflip:1; + unsigned int vflip:1; + unsigned int sleep:1; + unsigned int exposure; + u8 blue_balance; + u8 red_balance; + u8 i2c_reg_page; +}; + +struct sr030pc30_format { + enum v4l2_mbus_pixelcode code; + enum v4l2_colorspace colorspace; + u16 ispctl1_reg; +}; + +struct sr030pc30_frmsize { + u16 width; + u16 height; + int vid_ctl1; +}; + +struct i2c_regval { + u16 addr; + u16 val; +}; + +static const struct v4l2_queryctrl sr030pc30_ctrl[] = { + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto White Balance", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red Balance", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 64, + .flags = 0, + }, { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue Balance", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 64, + }, { + .id = V4L2_CID_EXPOSURE_AUTO, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Auto Exposure", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = EXPOS_MIN_MS, + .maximum = EXPOS_MAX_MS, + .step = 1, + .default_value = 1, + }, { + } +}; + +/* supported resolutions */ +static const struct sr030pc30_frmsize sr030pc30_sizes[] = { + { + .width = 640, + .height = 480, + .vid_ctl1 = SUBSAMPL_NONE_VGA, + }, { + .width = 320, + .height = 240, + .vid_ctl1 = SUBSAMPL_QVGA, + }, { + .width = 160, + .height = 120, + .vid_ctl1 = SUBSAMPL_QQVGA, + }, +}; + +/* supported pixel formats */ +static const struct sr030pc30_format sr030pc30_formats[] = { + { + .code = V4L2_MBUS_FMT_YUYV8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .ispctl1_reg = 0x03, + }, { + .code = V4L2_MBUS_FMT_YVYU8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .ispctl1_reg = 0x02, + }, { + .code = V4L2_MBUS_FMT_VYUY8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .ispctl1_reg = 0, + }, { + .code = V4L2_MBUS_FMT_UYVY8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .ispctl1_reg = 0x01, + }, { + .code = V4L2_MBUS_FMT_RGB565_2X8_BE, + .colorspace = V4L2_COLORSPACE_JPEG, + .ispctl1_reg = 0x40, + }, +}; + +static const struct i2c_regval sr030pc30_base_regs[] = { + /* Window size and position within pixel matrix */ + { WIN_ROWH_REG, 0x00 }, { WIN_ROWL_REG, 0x06 }, + { WIN_COLH_REG, 0x00 }, { WIN_COLL_REG, 0x06 }, + { WIN_HEIGHTH_REG, 0x01 }, { WIN_HEIGHTL_REG, 0xE0 }, + { WIN_WIDTHH_REG, 0x02 }, { WIN_WIDTHL_REG, 0x80 }, + { HBLANKH_REG, 0x01 }, { HBLANKL_REG, 0x50 }, + { VSYNCH_REG, 0x00 }, { VSYNCL_REG, 0x14 }, + { SYNC_CTL_REG, 0 }, + /* Color corection and saturation */ + { ISP_CTL_REG(0), 0x30 }, { YOFS_REG, 0x80 }, + { DARK_YOFS_REG, 0x04 }, { AG_ABRTH_REG, 0x78 }, + { SAT_CTL_REG, 0x1F }, { BSAT_REG, 0x90 }, + { AG_SAT_TH_REG, 0xF0 }, { 0x1064, 0x80 }, + { CMC_CTL_REG, 0x03 }, { CMC_OFSGH_REG, 0x3C }, + { CMC_OFSGL_REG, 0x2C }, { CMC_SIGN_REG, 0x2F }, + { CMC_COEF_REG(0), 0xCB }, { CMC_OFS_REG(0), 0x87 }, + { CMC_COEF_REG(1), 0x61 }, { CMC_OFS_REG(1), 0x18 }, + { CMC_COEF_REG(2), 0x16 }, { CMC_OFS_REG(2), 0x91 }, + { CMC_COEF_REG(3), 0x23 }, { CMC_OFS_REG(3), 0x94 }, + { CMC_COEF_REG(4), 0xCE }, { CMC_OFS_REG(4), 0x9f }, + { CMC_COEF_REG(5), 0x2B }, { CMC_OFS_REG(5), 0x33 }, + { CMC_COEF_REG(6), 0x01 }, { CMC_OFS_REG(6), 0x00 }, + { CMC_COEF_REG(7), 0x34 }, { CMC_OFS_REG(7), 0x94 }, + { CMC_COEF_REG(8), 0x75 }, { CMC_OFS_REG(8), 0x14 }, + /* Color corection coefficients */ + { GMA_CTL_REG, 0x03 }, { GMA_COEF_REG(0), 0x00 }, + { GMA_COEF_REG(1), 0x19 }, { GMA_COEF_REG(2), 0x26 }, + { GMA_COEF_REG(3), 0x3B }, { GMA_COEF_REG(4), 0x5D }, + { GMA_COEF_REG(5), 0x79 }, { GMA_COEF_REG(6), 0x8E }, + { GMA_COEF_REG(7), 0x9F }, { GMA_COEF_REG(8), 0xAF }, + { GMA_COEF_REG(9), 0xBD }, { GMA_COEF_REG(10), 0xCA }, + { GMA_COEF_REG(11), 0xDD }, { GMA_COEF_REG(12), 0xEC }, + { GMA_COEF_REG(13), 0xF7 }, { GMA_COEF_REG(14), 0xFF }, + /* Noise reduction, Z-LPF, YC-LPF and BLPF filters setup */ + { ZLPF_CTRL_REG, 0x99 }, { ZLPF_CTRL2_REG, 0x0E }, + { ZLPF_AGH_THR_REG, 0x29 }, { ZLPF_THR_REG, 0x0F }, + { ZLPF_DYN_THR_REG, 0x63 }, { YCLPF_CTL1_REG, 0x23 }, + { YCLPF_CTL2_REG, 0x3B }, { YCLPF_THR_REG, 0x05 }, + { BLPF_CTL_REG, 0x1D }, { BLPF_THR1_REG, 0x05 }, + { BLPF_THR2_REG, 0x04 }, + /* Automatic white balance */ + { AWB_CTL1_REG, 0xFB }, { AWB_CTL2_REG, 0x26 }, + { AWB_RMAX_REG, 0x54 }, { AWB_RMIN_REG, 0x2B }, + { AWB_BMAX_REG, 0x57 }, { AWB_BMIN_REG, 0x29 }, + { AWB_RMAXB_REG, 0x50 }, { AWB_RMINB_REG, 0x43 }, + { AWB_BMAXB_REG, 0x30 }, { AWB_BMINB_REG, 0x22 }, + /* Auto exposure */ + { AE_CTL1_REG, 0x8C }, { AE_CTL2_REG, 0x04 }, + { AE_FRM_CTL_REG, 0x01 }, { AE_FINE_CTL_REG(0), 0x3F }, + { AE_FINE_CTL_REG(1), 0xA3 }, { AE_FINE_CTL_REG(3), 0x34 }, + /* Lens shading compensation */ + { LENS_CTRL_REG, 0x01 }, { LENS_XCEN_REG, 0x80 }, + { LENS_YCEN_REG, 0x70 }, { LENS_R_COMP_REG, 0x53 }, + { LENS_G_COMP_REG, 0x40 }, { LENS_B_COMP_REG, 0x3e }, + { REG_TERM, 0 }, +}; + +static inline struct sr030pc30_info *to_sr030pc30(struct v4l2_subdev *sd) +{ + return container_of(sd, struct sr030pc30_info, sd); +} + +static inline int set_i2c_page(struct sr030pc30_info *info, + struct i2c_client *client, unsigned int reg) +{ + int ret = 0; + u32 page = reg >> 8 & 0xFF; + + if (info->i2c_reg_page != page && (reg & 0xFF) != 0x03) { + ret = i2c_smbus_write_byte_data(client, PAGEMODE_REG, page); + if (!ret) + info->i2c_reg_page = page; + } + return ret; +} + +static int cam_i2c_read(struct v4l2_subdev *sd, u32 reg_addr) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct sr030pc30_info *info = to_sr030pc30(sd); + + int ret = set_i2c_page(info, client, reg_addr); + if (!ret) + ret = i2c_smbus_read_byte_data(client, reg_addr & 0xFF); + return ret; +} + +static int cam_i2c_write(struct v4l2_subdev *sd, u32 reg_addr, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct sr030pc30_info *info = to_sr030pc30(sd); + + int ret = set_i2c_page(info, client, reg_addr); + if (!ret) + ret = i2c_smbus_write_byte_data( + client, reg_addr & 0xFF, val); + return ret; +} + +static inline int sr030pc30_bulk_write_reg(struct v4l2_subdev *sd, + const struct i2c_regval *msg) +{ + while (msg->addr != REG_TERM) { + int ret = cam_i2c_write(sd, msg->addr, msg->val); + if (ret) + return ret; + msg++; + } + return 0; +} + +/* Device reset and sleep mode control */ +static int sr030pc30_pwr_ctrl(struct v4l2_subdev *sd, + bool reset, bool sleep) +{ + struct sr030pc30_info *info = to_sr030pc30(sd); + u8 reg = sleep ? 0xF1 : 0xF0; + int ret = 0; + + if (reset) + ret = cam_i2c_write(sd, POWER_CTRL_REG, reg | 0x02); + if (!ret) { + ret = cam_i2c_write(sd, POWER_CTRL_REG, reg); + if (!ret) { + info->sleep = sleep; + if (reset) + info->i2c_reg_page = -1; + } + } + return ret; +} + +static inline int sr030pc30_enable_autoexposure(struct v4l2_subdev *sd, int on) +{ + struct sr030pc30_info *info = to_sr030pc30(sd); + /* auto anti-flicker is also enabled here */ + int ret = cam_i2c_write(sd, AE_CTL1_REG, on ? 0xDC : 0x0C); + if (!ret) + info->auto_exp = on; + return ret; +} + +static int sr030pc30_set_exposure(struct v4l2_subdev *sd, int value) +{ + struct sr030pc30_info *info = to_sr030pc30(sd); + + unsigned long expos = value * info->pdata->clk_rate / (8 * 1000); + + int ret = cam_i2c_write(sd, EXP_TIMEH_REG, expos >> 16 & 0xFF); + if (!ret) + ret = cam_i2c_write(sd, EXP_TIMEM_REG, expos >> 8 & 0xFF); + if (!ret) + ret = cam_i2c_write(sd, EXP_TIMEL_REG, expos & 0xFF); + if (!ret) { /* Turn off AE */ + info->exposure = value; + ret = sr030pc30_enable_autoexposure(sd, 0); + } + return ret; +} + +/* Automatic white balance control */ +static int sr030pc30_enable_autowhitebalance(struct v4l2_subdev *sd, int on) +{ + struct sr030pc30_info *info = to_sr030pc30(sd); + + int ret = cam_i2c_write(sd, AWB_CTL2_REG, on ? 0x2E : 0x2F); + if (!ret) + ret = cam_i2c_write(sd, AWB_CTL1_REG, on ? 0xFB : 0x7B); + if (!ret) + info->auto_wb = on; + + return ret; +} + +static int sr030pc30_set_flip(struct v4l2_subdev *sd) +{ + struct sr030pc30_info *info = to_sr030pc30(sd); + + s32 reg = cam_i2c_read(sd, VDO_CTL2_REG); + if (reg < 0) + return reg; + + reg &= 0x7C; + if (info->hflip) + reg |= 0x01; + if (info->vflip) + reg |= 0x02; + return cam_i2c_write(sd, VDO_CTL2_REG, reg | 0x80); +} + +/* Configure resolution, color format and image flip */ +static int sr030pc30_set_params(struct v4l2_subdev *sd) +{ + struct sr030pc30_info *info = to_sr030pc30(sd); + int ret; + + if (!info->curr_win) + return -EINVAL; + + /* Configure the resolution through subsampling */ + ret = cam_i2c_write(sd, VDO_CTL1_REG, + info->curr_win->vid_ctl1); + + if (!ret && info->curr_fmt) + ret = cam_i2c_write(sd, ISP_CTL_REG(0), + info->curr_fmt->ispctl1_reg); + if (!ret) + ret = sr030pc30_set_flip(sd); + + return ret; +} + +/* Find nearest matching image pixel size. */ +static int sr030pc30_try_frame_size(struct v4l2_mbus_framefmt *mf) +{ + unsigned int min_err = ~0; + int i = ARRAY_SIZE(sr030pc30_sizes); + const struct sr030pc30_frmsize *fsize = &sr030pc30_sizes[0], + *match = NULL; + while (i--) { + int err = abs(fsize->width - mf->width) + + abs(fsize->height - mf->height); + if (err < min_err) { + min_err = err; + match = fsize; + } + fsize++; + } + if (match) { + mf->width = match->width; + mf->height = match->height; + return 0; + } + return -EINVAL; +} + +static int sr030pc30_queryctrl(struct v4l2_subdev *sd, + struct v4l2_queryctrl *qc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sr030pc30_ctrl); i++) + if (qc->id == sr030pc30_ctrl[i].id) { + *qc = sr030pc30_ctrl[i]; + v4l2_dbg(1, debug, sd, "%s id: %d\n", + __func__, qc->id); + return 0; + } + + return -EINVAL; +} + +static inline int sr030pc30_set_bluebalance(struct v4l2_subdev *sd, int value) +{ + int ret = cam_i2c_write(sd, MWB_BGAIN_REG, value); + if (!ret) + to_sr030pc30(sd)->blue_balance = value; + return ret; +} + +static inline int sr030pc30_set_redbalance(struct v4l2_subdev *sd, int value) +{ + int ret = cam_i2c_write(sd, MWB_RGAIN_REG, value); + if (!ret) + to_sr030pc30(sd)->red_balance = value; + return ret; +} + +static int sr030pc30_s_ctrl(struct v4l2_subdev *sd, + struct v4l2_control *ctrl) +{ + int i, ret = 0; + + for (i = 0; i < ARRAY_SIZE(sr030pc30_ctrl); i++) + if (ctrl->id == sr030pc30_ctrl[i].id) + break; + + if (i == ARRAY_SIZE(sr030pc30_ctrl)) + return -EINVAL; + + if (ctrl->value < sr030pc30_ctrl[i].minimum || + ctrl->value > sr030pc30_ctrl[i].maximum) + return -ERANGE; + + v4l2_dbg(1, debug, sd, "%s: ctrl_id: %d, value: %d\n", + __func__, ctrl->id, ctrl->value); + + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + sr030pc30_enable_autowhitebalance(sd, ctrl->value); + break; + case V4L2_CID_BLUE_BALANCE: + ret = sr030pc30_set_bluebalance(sd, ctrl->value); + break; + case V4L2_CID_RED_BALANCE: + ret = sr030pc30_set_redbalance(sd, ctrl->value); + break; + case V4L2_CID_EXPOSURE_AUTO: + sr030pc30_enable_autoexposure(sd, + ctrl->value == V4L2_EXPOSURE_AUTO); + break; + case V4L2_CID_EXPOSURE: + ret = sr030pc30_set_exposure(sd, ctrl->value); + break; + default: + return -EINVAL; + } + + return ret; +} + +static int sr030pc30_g_ctrl(struct v4l2_subdev *sd, + struct v4l2_control *ctrl) +{ + struct sr030pc30_info *info = to_sr030pc30(sd); + + v4l2_dbg(1, debug, sd, "%s: id: %d\n", __func__, ctrl->id); + + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + ctrl->value = info->auto_wb; + break; + case V4L2_CID_BLUE_BALANCE: + ctrl->value = info->blue_balance; + break; + case V4L2_CID_RED_BALANCE: + ctrl->value = info->red_balance; + break; + case V4L2_CID_EXPOSURE_AUTO: + ctrl->value = info->auto_exp; + break; + case V4L2_CID_EXPOSURE: + ctrl->value = info->exposure; + break; + default: + return -EINVAL; + } + return 0; +} + +static int sr030pc30_enum_fmt(struct v4l2_subdev *sd, unsigned int index, + enum v4l2_mbus_pixelcode *code) +{ + if (!code || index >= ARRAY_SIZE(sr030pc30_formats)) + return -EINVAL; + + *code = sr030pc30_formats[index].code; + return 0; +} + +static int sr030pc30_g_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + struct sr030pc30_info *info = to_sr030pc30(sd); + int ret; + + if (!mf) + return -EINVAL; + + if (!info->curr_win || !info->curr_fmt) { + ret = sr030pc30_set_params(sd); + if (ret) + return ret; + } + + mf->width = info->curr_win->width; + mf->height = info->curr_win->height; + mf->code = info->curr_fmt->code; + mf->colorspace = info->curr_fmt->colorspace; + mf->field = V4L2_FIELD_NONE; + + return 0; +} + +/* Return nearest media bus frame format. */ +static const struct sr030pc30_format *try_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + int i = ARRAY_SIZE(sr030pc30_formats); + + sr030pc30_try_frame_size(mf); + + while (i--) + if (mf->code == sr030pc30_formats[i].code) + break; + + mf->code = sr030pc30_formats[i].code; + + return &sr030pc30_formats[i]; +} + +/* Return nearest media bus frame format. */ +static int sr030pc30_try_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + if (!sd || !mf) + return -EINVAL; + + try_fmt(sd, mf); + return 0; +} + +static int sr030pc30_s_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + struct sr030pc30_info *info = to_sr030pc30(sd); + + if (!sd || !mf) + return -EINVAL; + + info->curr_fmt = try_fmt(sd, mf); + + return sr030pc30_set_params(sd); +} + +static int sr030pc30_base_config(struct v4l2_subdev *sd) +{ + struct sr030pc30_info *info = to_sr030pc30(sd); + int ret; + unsigned long expmin, expmax; + + ret = sr030pc30_bulk_write_reg(sd, sr030pc30_base_regs); + if (!ret) { + info->curr_fmt = &sr030pc30_formats[0]; + info->curr_win = &sr030pc30_sizes[0]; + ret = sr030pc30_set_params(sd); + } + if (!ret) + ret = sr030pc30_pwr_ctrl(sd, false, false); + + if (!ret && !info->pdata) + return ret; + + expmin = EXPOS_MIN_MS * info->pdata->clk_rate / (8 * 1000); + expmax = EXPOS_MAX_MS * info->pdata->clk_rate / (8 * 1000); + + v4l2_dbg(1, debug, sd, "%s: expmin= %lx, expmax= %lx", __func__, + expmin, expmax); + + /* Setting up manual exposure time range */ + ret = cam_i2c_write(sd, EXP_MMINH_REG, expmin >> 8 & 0xFF); + if (!ret) + ret = cam_i2c_write(sd, EXP_MMINL_REG, expmin & 0xFF); + if (!ret) + ret = cam_i2c_write(sd, EXP_MMAXH_REG, expmax >> 16 & 0xFF); + if (!ret) + ret = cam_i2c_write(sd, EXP_MMAXM_REG, expmax >> 8 & 0xFF); + if (!ret) + ret = cam_i2c_write(sd, EXP_MMAXL_REG, expmax & 0xFF); + + return ret; +} + +static int sr030pc30_s_power(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct sr030pc30_info *info = to_sr030pc30(sd); + const struct sr030pc30_platform_data *pdata = info->pdata; + int ret; + + if (pdata == NULL) { + WARN(1, "No platform data!\n"); + return -EINVAL; + } + + /* + * Put sensor into power sleep mode before switching off + * power and disabling MCLK. + */ + if (!on) + sr030pc30_pwr_ctrl(sd, false, true); + + /* set_power controls sensor's power and clock */ + if (pdata->set_power) { + ret = pdata->set_power(&client->dev, on); + if (ret) + return ret; + } + + if (on) { + ret = sr030pc30_base_config(sd); + } else { + ret = 0; + info->curr_win = NULL; + info->curr_fmt = NULL; + } + + return ret; +} + +static const struct v4l2_subdev_core_ops sr030pc30_core_ops = { + .s_power = sr030pc30_s_power, + .queryctrl = sr030pc30_queryctrl, + .s_ctrl = sr030pc30_s_ctrl, + .g_ctrl = sr030pc30_g_ctrl, +}; + +static const struct v4l2_subdev_video_ops sr030pc30_video_ops = { + .g_mbus_fmt = sr030pc30_g_fmt, + .s_mbus_fmt = sr030pc30_s_fmt, + .try_mbus_fmt = sr030pc30_try_fmt, + .enum_mbus_fmt = sr030pc30_enum_fmt, +}; + +static const struct v4l2_subdev_ops sr030pc30_ops = { + .core = &sr030pc30_core_ops, + .video = &sr030pc30_video_ops, +}; + +/* + * Detect sensor type. Return 0 if SR030PC30 was detected + * or -ENODEV otherwise. + */ +static int sr030pc30_detect(struct i2c_client *client) +{ + const struct sr030pc30_platform_data *pdata + = client->dev.platform_data; + int ret; + + /* Enable sensor's power and clock */ + if (pdata->set_power) { + ret = pdata->set_power(&client->dev, 1); + if (ret) + return ret; + } + + ret = i2c_smbus_read_byte_data(client, DEVICE_ID_REG); + + if (pdata->set_power) + pdata->set_power(&client->dev, 0); + + if (ret < 0) { + dev_err(&client->dev, "%s: I2C read failed\n", __func__); + return ret; + } + + return ret == SR030PC30_ID ? 0 : -ENODEV; +} + + +static int sr030pc30_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct sr030pc30_info *info; + struct v4l2_subdev *sd; + const struct sr030pc30_platform_data *pdata + = client->dev.platform_data; + int ret; + + if (!pdata) { + dev_err(&client->dev, "No platform data!"); + return -EIO; + } + + ret = sr030pc30_detect(client); + if (ret) + return ret; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + sd = &info->sd; + strcpy(sd->name, MODULE_NAME); + info->pdata = client->dev.platform_data; + + v4l2_i2c_subdev_init(sd, client, &sr030pc30_ops); + + info->i2c_reg_page = -1; + info->hflip = 1; + info->auto_exp = 1; + info->exposure = 30; + + return 0; +} + +static int sr030pc30_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct sr030pc30_info *info = to_sr030pc30(sd); + + v4l2_device_unregister_subdev(sd); + kfree(info); + return 0; +} + +static const struct i2c_device_id sr030pc30_id[] = { + { MODULE_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, sr030pc30_id); + + +static struct i2c_driver sr030pc30_i2c_driver = { + .driver = { + .name = MODULE_NAME + }, + .probe = sr030pc30_probe, + .remove = sr030pc30_remove, + .id_table = sr030pc30_id, +}; + +module_i2c_driver(sr030pc30_i2c_driver); + +MODULE_DESCRIPTION("Siliconfile SR030PC30 camera driver"); +MODULE_AUTHOR("Sylwester Nawrocki "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/tcm825x.c b/drivers/media/i2c/tcm825x.c new file mode 100644 index 00000000000..9252529fc5d --- /dev/null +++ b/drivers/media/i2c/tcm825x.c @@ -0,0 +1,937 @@ +/* + * drivers/media/i2c/tcm825x.c + * + * TCM825X camera sensor driver. + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Sakari Ailus + * + * Based on code from David Cohen + * + * This driver was based on ov9640 sensor driver from MontaVista + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include + +#include "tcm825x.h" + +/* + * The sensor has two fps modes: the lower one just gives half the fps + * at the same xclk than the high one. + */ +#define MAX_FPS 30 +#define MIN_FPS 8 +#define MAX_HALF_FPS (MAX_FPS / 2) +#define HIGH_FPS_MODE_LOWER_LIMIT 14 +#define DEFAULT_FPS MAX_HALF_FPS + +struct tcm825x_sensor { + const struct tcm825x_platform_data *platform_data; + struct v4l2_int_device *v4l2_int_device; + struct i2c_client *i2c_client; + struct v4l2_pix_format pix; + struct v4l2_fract timeperframe; +}; + +/* list of image formats supported by TCM825X sensor */ +static const struct v4l2_fmtdesc tcm825x_formats[] = { + { + .description = "YUYV (YUV 4:2:2), packed", + .pixelformat = V4L2_PIX_FMT_UYVY, + }, { + /* Note: V4L2 defines RGB565 as: + * + * Byte 0 Byte 1 + * g2 g1 g0 r4 r3 r2 r1 r0 b4 b3 b2 b1 b0 g5 g4 g3 + * + * We interpret RGB565 as: + * + * Byte 0 Byte 1 + * g2 g1 g0 b4 b3 b2 b1 b0 r4 r3 r2 r1 r0 g5 g4 g3 + */ + .description = "RGB565, le", + .pixelformat = V4L2_PIX_FMT_RGB565, + }, +}; + +#define TCM825X_NUM_CAPTURE_FORMATS ARRAY_SIZE(tcm825x_formats) + +/* + * TCM825X register configuration for all combinations of pixel format and + * image size + */ +static const struct tcm825x_reg subqcif = { 0x20, TCM825X_PICSIZ }; +static const struct tcm825x_reg qcif = { 0x18, TCM825X_PICSIZ }; +static const struct tcm825x_reg cif = { 0x14, TCM825X_PICSIZ }; +static const struct tcm825x_reg qqvga = { 0x0c, TCM825X_PICSIZ }; +static const struct tcm825x_reg qvga = { 0x04, TCM825X_PICSIZ }; +static const struct tcm825x_reg vga = { 0x00, TCM825X_PICSIZ }; + +static const struct tcm825x_reg yuv422 = { 0x00, TCM825X_PICFMT }; +static const struct tcm825x_reg rgb565 = { 0x02, TCM825X_PICFMT }; + +/* Our own specific controls */ +#define V4L2_CID_ALC V4L2_CID_PRIVATE_BASE +#define V4L2_CID_H_EDGE_EN V4L2_CID_PRIVATE_BASE + 1 +#define V4L2_CID_V_EDGE_EN V4L2_CID_PRIVATE_BASE + 2 +#define V4L2_CID_LENS V4L2_CID_PRIVATE_BASE + 3 +#define V4L2_CID_MAX_EXPOSURE_TIME V4L2_CID_PRIVATE_BASE + 4 +#define V4L2_CID_LAST_PRIV V4L2_CID_MAX_EXPOSURE_TIME + +/* Video controls */ +static struct vcontrol { + struct v4l2_queryctrl qc; + u16 reg; + u16 start_bit; +} video_control[] = { + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 63, + .step = 1, + }, + .reg = TCM825X_AG, + .start_bit = 0, + }, + { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red Balance", + .minimum = 0, + .maximum = 255, + .step = 1, + }, + .reg = TCM825X_MRG, + .start_bit = 0, + }, + { + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue Balance", + .minimum = 0, + .maximum = 255, + .step = 1, + }, + .reg = TCM825X_MBG, + .start_bit = 0, + }, + { + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto White Balance", + .minimum = 0, + .maximum = 1, + .step = 0, + }, + .reg = TCM825X_AWBSW, + .start_bit = 7, + }, + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure Time", + .minimum = 0, + .maximum = 0x1fff, + .step = 1, + }, + .reg = TCM825X_ESRSPD_U, + .start_bit = 0, + }, + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mirror Image", + .minimum = 0, + .maximum = 1, + .step = 0, + }, + .reg = TCM825X_H_INV, + .start_bit = 6, + }, + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vertical Flip", + .minimum = 0, + .maximum = 1, + .step = 0, + }, + .reg = TCM825X_V_INV, + .start_bit = 7, + }, + /* Private controls */ + { + { + .id = V4L2_CID_ALC, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Luminance Control", + .minimum = 0, + .maximum = 1, + .step = 0, + }, + .reg = TCM825X_ALCSW, + .start_bit = 7, + }, + { + { + .id = V4L2_CID_H_EDGE_EN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Horizontal Edge Enhancement", + .minimum = 0, + .maximum = 0xff, + .step = 1, + }, + .reg = TCM825X_HDTG, + .start_bit = 0, + }, + { + { + .id = V4L2_CID_V_EDGE_EN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Vertical Edge Enhancement", + .minimum = 0, + .maximum = 0xff, + .step = 1, + }, + .reg = TCM825X_VDTG, + .start_bit = 0, + }, + { + { + .id = V4L2_CID_LENS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Lens Shading Compensation", + .minimum = 0, + .maximum = 0x3f, + .step = 1, + }, + .reg = TCM825X_LENS, + .start_bit = 0, + }, + { + { + .id = V4L2_CID_MAX_EXPOSURE_TIME, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Maximum Exposure Time", + .minimum = 0, + .maximum = 0x3, + .step = 1, + }, + .reg = TCM825X_ESRLIM, + .start_bit = 5, + }, +}; + + +static const struct tcm825x_reg *tcm825x_siz_reg[NUM_IMAGE_SIZES] = +{ &subqcif, &qqvga, &qcif, &qvga, &cif, &vga }; + +static const struct tcm825x_reg *tcm825x_fmt_reg[NUM_PIXEL_FORMATS] = +{ &yuv422, &rgb565 }; + +/* + * Read a value from a register in an TCM825X sensor device. The value is + * returned in 'val'. + * Returns zero if successful, or non-zero otherwise. + */ +static int tcm825x_read_reg(struct i2c_client *client, int reg) +{ + int err; + struct i2c_msg msg[2]; + u8 reg_buf, data_buf = 0; + + if (!client->adapter) + return -ENODEV; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = ®_buf; + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 1; + msg[1].buf = &data_buf; + + reg_buf = reg; + + err = i2c_transfer(client->adapter, msg, 2); + if (err < 0) + return err; + return data_buf; +} + +/* + * Write a value to a register in an TCM825X sensor device. + * Returns zero if successful, or non-zero otherwise. + */ +static int tcm825x_write_reg(struct i2c_client *client, u8 reg, u8 val) +{ + int err; + struct i2c_msg msg[1]; + unsigned char data[2]; + + if (!client->adapter) + return -ENODEV; + + msg->addr = client->addr; + msg->flags = 0; + msg->len = 2; + msg->buf = data; + data[0] = reg; + data[1] = val; + err = i2c_transfer(client->adapter, msg, 1); + if (err >= 0) + return 0; + return err; +} + +static int __tcm825x_write_reg_mask(struct i2c_client *client, + u8 reg, u8 val, u8 mask) +{ + int rc; + + /* need to do read - modify - write */ + rc = tcm825x_read_reg(client, reg); + if (rc < 0) + return rc; + + rc &= (~mask); /* Clear the masked bits */ + val &= mask; /* Enforce mask on value */ + val |= rc; + + /* write the new value to the register */ + rc = tcm825x_write_reg(client, reg, val); + if (rc) + return rc; + + return 0; +} + +#define tcm825x_write_reg_mask(client, regmask, val) \ + __tcm825x_write_reg_mask(client, TCM825X_ADDR((regmask)), val, \ + TCM825X_MASK((regmask))) + + +/* + * Initialize a list of TCM825X registers. + * The list of registers is terminated by the pair of values + * { TCM825X_REG_TERM, TCM825X_VAL_TERM }. + * Returns zero if successful, or non-zero otherwise. + */ +static int tcm825x_write_default_regs(struct i2c_client *client, + const struct tcm825x_reg *reglist) +{ + int err; + const struct tcm825x_reg *next = reglist; + + while (!((next->reg == TCM825X_REG_TERM) + && (next->val == TCM825X_VAL_TERM))) { + err = tcm825x_write_reg(client, next->reg, next->val); + if (err) { + dev_err(&client->dev, "register writing failed\n"); + return err; + } + next++; + } + + return 0; +} + +static struct vcontrol *find_vctrl(int id) +{ + int i; + + if (id < V4L2_CID_BASE) + return NULL; + + for (i = 0; i < ARRAY_SIZE(video_control); i++) + if (video_control[i].qc.id == id) + return &video_control[i]; + + return NULL; +} + +/* + * Find the best match for a requested image capture size. The best match + * is chosen as the nearest match that has the same number or fewer pixels + * as the requested size, or the smallest image size if the requested size + * has fewer pixels than the smallest image. + */ +static enum image_size tcm825x_find_size(struct v4l2_int_device *s, + unsigned int width, + unsigned int height) +{ + enum image_size isize; + unsigned long pixels = width * height; + struct tcm825x_sensor *sensor = s->priv; + + for (isize = subQCIF; isize < VGA; isize++) { + if (tcm825x_sizes[isize + 1].height + * tcm825x_sizes[isize + 1].width > pixels) { + dev_dbg(&sensor->i2c_client->dev, "size %d\n", isize); + + return isize; + } + } + + dev_dbg(&sensor->i2c_client->dev, "format default VGA\n"); + + return VGA; +} + +/* + * Configure the TCM825X for current image size, pixel format, and + * frame period. fper is the frame period (in seconds) expressed as a + * fraction. Returns zero if successful, or non-zero otherwise. The + * actual frame period is returned in fper. + */ +static int tcm825x_configure(struct v4l2_int_device *s) +{ + struct tcm825x_sensor *sensor = s->priv; + struct v4l2_pix_format *pix = &sensor->pix; + enum image_size isize = tcm825x_find_size(s, pix->width, pix->height); + struct v4l2_fract *fper = &sensor->timeperframe; + enum pixel_format pfmt; + int err; + u32 tgt_fps; + u8 val; + + /* common register initialization */ + err = tcm825x_write_default_regs( + sensor->i2c_client, sensor->platform_data->default_regs()); + if (err) + return err; + + /* configure image size */ + val = tcm825x_siz_reg[isize]->val; + dev_dbg(&sensor->i2c_client->dev, + "configuring image size %d\n", isize); + err = tcm825x_write_reg_mask(sensor->i2c_client, + tcm825x_siz_reg[isize]->reg, val); + if (err) + return err; + + /* configure pixel format */ + switch (pix->pixelformat) { + default: + case V4L2_PIX_FMT_RGB565: + pfmt = RGB565; + break; + case V4L2_PIX_FMT_UYVY: + pfmt = YUV422; + break; + } + + dev_dbg(&sensor->i2c_client->dev, + "configuring pixel format %d\n", pfmt); + val = tcm825x_fmt_reg[pfmt]->val; + + err = tcm825x_write_reg_mask(sensor->i2c_client, + tcm825x_fmt_reg[pfmt]->reg, val); + if (err) + return err; + + /* + * For frame rate < 15, the FPS reg (addr 0x02, bit 7) must be + * set. Frame rate will be halved from the normal. + */ + tgt_fps = fper->denominator / fper->numerator; + if (tgt_fps <= HIGH_FPS_MODE_LOWER_LIMIT) { + val = tcm825x_read_reg(sensor->i2c_client, 0x02); + val |= 0x80; + tcm825x_write_reg(sensor->i2c_client, 0x02, val); + } + + return 0; +} + +static int ioctl_queryctrl(struct v4l2_int_device *s, + struct v4l2_queryctrl *qc) +{ + struct vcontrol *control; + + control = find_vctrl(qc->id); + + if (control == NULL) + return -EINVAL; + + *qc = control->qc; + + return 0; +} + +static int ioctl_g_ctrl(struct v4l2_int_device *s, + struct v4l2_control *vc) +{ + struct tcm825x_sensor *sensor = s->priv; + struct i2c_client *client = sensor->i2c_client; + int val, r; + struct vcontrol *lvc; + + /* exposure time is special, spread across 2 registers */ + if (vc->id == V4L2_CID_EXPOSURE) { + int val_lower, val_upper; + + val_upper = tcm825x_read_reg(client, + TCM825X_ADDR(TCM825X_ESRSPD_U)); + if (val_upper < 0) + return val_upper; + val_lower = tcm825x_read_reg(client, + TCM825X_ADDR(TCM825X_ESRSPD_L)); + if (val_lower < 0) + return val_lower; + + vc->value = ((val_upper & 0x1f) << 8) | (val_lower); + return 0; + } + + lvc = find_vctrl(vc->id); + if (lvc == NULL) + return -EINVAL; + + r = tcm825x_read_reg(client, TCM825X_ADDR(lvc->reg)); + if (r < 0) + return r; + val = r & TCM825X_MASK(lvc->reg); + val >>= lvc->start_bit; + + if (val < 0) + return val; + + if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP) + val ^= sensor->platform_data->is_upside_down(); + + vc->value = val; + return 0; +} + +static int ioctl_s_ctrl(struct v4l2_int_device *s, + struct v4l2_control *vc) +{ + struct tcm825x_sensor *sensor = s->priv; + struct i2c_client *client = sensor->i2c_client; + struct vcontrol *lvc; + int val = vc->value; + + /* exposure time is special, spread across 2 registers */ + if (vc->id == V4L2_CID_EXPOSURE) { + int val_lower, val_upper; + val_lower = val & TCM825X_MASK(TCM825X_ESRSPD_L); + val_upper = (val >> 8) & TCM825X_MASK(TCM825X_ESRSPD_U); + + if (tcm825x_write_reg_mask(client, + TCM825X_ESRSPD_U, val_upper)) + return -EIO; + + if (tcm825x_write_reg_mask(client, + TCM825X_ESRSPD_L, val_lower)) + return -EIO; + + return 0; + } + + lvc = find_vctrl(vc->id); + if (lvc == NULL) + return -EINVAL; + + if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP) + val ^= sensor->platform_data->is_upside_down(); + + val = val << lvc->start_bit; + if (tcm825x_write_reg_mask(client, lvc->reg, val)) + return -EIO; + + return 0; +} + +static int ioctl_enum_fmt_cap(struct v4l2_int_device *s, + struct v4l2_fmtdesc *fmt) +{ + int index = fmt->index; + + switch (fmt->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (index >= TCM825X_NUM_CAPTURE_FORMATS) + return -EINVAL; + break; + + default: + return -EINVAL; + } + + fmt->flags = tcm825x_formats[index].flags; + strlcpy(fmt->description, tcm825x_formats[index].description, + sizeof(fmt->description)); + fmt->pixelformat = tcm825x_formats[index].pixelformat; + + return 0; +} + +static int ioctl_try_fmt_cap(struct v4l2_int_device *s, + struct v4l2_format *f) +{ + struct tcm825x_sensor *sensor = s->priv; + enum image_size isize; + int ifmt; + struct v4l2_pix_format *pix = &f->fmt.pix; + + isize = tcm825x_find_size(s, pix->width, pix->height); + dev_dbg(&sensor->i2c_client->dev, "isize = %d num_capture = %lu\n", + isize, (unsigned long)TCM825X_NUM_CAPTURE_FORMATS); + + pix->width = tcm825x_sizes[isize].width; + pix->height = tcm825x_sizes[isize].height; + + for (ifmt = 0; ifmt < TCM825X_NUM_CAPTURE_FORMATS; ifmt++) + if (pix->pixelformat == tcm825x_formats[ifmt].pixelformat) + break; + + if (ifmt == TCM825X_NUM_CAPTURE_FORMATS) + ifmt = 0; /* Default = YUV 4:2:2 */ + + pix->pixelformat = tcm825x_formats[ifmt].pixelformat; + pix->field = V4L2_FIELD_NONE; + pix->bytesperline = pix->width * TCM825X_BYTES_PER_PIXEL; + pix->sizeimage = pix->bytesperline * pix->height; + pix->priv = 0; + dev_dbg(&sensor->i2c_client->dev, "format = 0x%08x\n", + pix->pixelformat); + + switch (pix->pixelformat) { + case V4L2_PIX_FMT_UYVY: + default: + pix->colorspace = V4L2_COLORSPACE_JPEG; + break; + case V4L2_PIX_FMT_RGB565: + pix->colorspace = V4L2_COLORSPACE_SRGB; + break; + } + + return 0; +} + +static int ioctl_s_fmt_cap(struct v4l2_int_device *s, + struct v4l2_format *f) +{ + struct tcm825x_sensor *sensor = s->priv; + struct v4l2_pix_format *pix = &f->fmt.pix; + int rval; + + rval = ioctl_try_fmt_cap(s, f); + if (rval) + return rval; + + rval = tcm825x_configure(s); + + sensor->pix = *pix; + + return rval; +} + +static int ioctl_g_fmt_cap(struct v4l2_int_device *s, + struct v4l2_format *f) +{ + struct tcm825x_sensor *sensor = s->priv; + + f->fmt.pix = sensor->pix; + + return 0; +} + +static int ioctl_g_parm(struct v4l2_int_device *s, + struct v4l2_streamparm *a) +{ + struct tcm825x_sensor *sensor = s->priv; + struct v4l2_captureparm *cparm = &a->parm.capture; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memset(a, 0, sizeof(*a)); + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + cparm->capability = V4L2_CAP_TIMEPERFRAME; + cparm->timeperframe = sensor->timeperframe; + + return 0; +} + +static int ioctl_s_parm(struct v4l2_int_device *s, + struct v4l2_streamparm *a) +{ + struct tcm825x_sensor *sensor = s->priv; + struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe; + u32 tgt_fps; /* target frames per secound */ + int rval; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if ((timeperframe->numerator == 0) + || (timeperframe->denominator == 0)) { + timeperframe->denominator = DEFAULT_FPS; + timeperframe->numerator = 1; + } + + tgt_fps = timeperframe->denominator / timeperframe->numerator; + + if (tgt_fps > MAX_FPS) { + timeperframe->denominator = MAX_FPS; + timeperframe->numerator = 1; + } else if (tgt_fps < MIN_FPS) { + timeperframe->denominator = MIN_FPS; + timeperframe->numerator = 1; + } + + sensor->timeperframe = *timeperframe; + + rval = tcm825x_configure(s); + + return rval; +} + +static int ioctl_s_power(struct v4l2_int_device *s, int on) +{ + struct tcm825x_sensor *sensor = s->priv; + + return sensor->platform_data->power_set(on); +} + +/* + * Given the image capture format in pix, the nominal frame period in + * timeperframe, calculate the required xclk frequency. + * + * TCM825X input frequency characteristics are: + * Minimum 11.9 MHz, Typical 24.57 MHz and maximum 25/27 MHz + */ + +static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) +{ + struct tcm825x_sensor *sensor = s->priv; + struct v4l2_fract *timeperframe = &sensor->timeperframe; + u32 tgt_xclk; /* target xclk */ + u32 tgt_fps; /* target frames per secound */ + int rval; + + rval = sensor->platform_data->ifparm(p); + if (rval) + return rval; + + tgt_fps = timeperframe->denominator / timeperframe->numerator; + + tgt_xclk = (tgt_fps <= HIGH_FPS_MODE_LOWER_LIMIT) ? + (2457 * tgt_fps) / MAX_HALF_FPS : + (2457 * tgt_fps) / MAX_FPS; + tgt_xclk *= 10000; + + tgt_xclk = min(tgt_xclk, (u32)TCM825X_XCLK_MAX); + tgt_xclk = max(tgt_xclk, (u32)TCM825X_XCLK_MIN); + + p->u.bt656.clock_curr = tgt_xclk; + + return 0; +} + +static int ioctl_g_needs_reset(struct v4l2_int_device *s, void *buf) +{ + struct tcm825x_sensor *sensor = s->priv; + + return sensor->platform_data->needs_reset(s, buf, &sensor->pix); +} + +static int ioctl_reset(struct v4l2_int_device *s) +{ + return -EBUSY; +} + +static int ioctl_init(struct v4l2_int_device *s) +{ + return tcm825x_configure(s); +} + +static int ioctl_dev_exit(struct v4l2_int_device *s) +{ + return 0; +} + +static int ioctl_dev_init(struct v4l2_int_device *s) +{ + struct tcm825x_sensor *sensor = s->priv; + int r; + + r = tcm825x_read_reg(sensor->i2c_client, 0x01); + if (r < 0) + return r; + if (r == 0) { + dev_err(&sensor->i2c_client->dev, "device not detected\n"); + return -EIO; + } + return 0; +} + +static struct v4l2_int_ioctl_desc tcm825x_ioctl_desc[] = { + { vidioc_int_dev_init_num, + (v4l2_int_ioctl_func *)ioctl_dev_init }, + { vidioc_int_dev_exit_num, + (v4l2_int_ioctl_func *)ioctl_dev_exit }, + { vidioc_int_s_power_num, + (v4l2_int_ioctl_func *)ioctl_s_power }, + { vidioc_int_g_ifparm_num, + (v4l2_int_ioctl_func *)ioctl_g_ifparm }, + { vidioc_int_g_needs_reset_num, + (v4l2_int_ioctl_func *)ioctl_g_needs_reset }, + { vidioc_int_reset_num, + (v4l2_int_ioctl_func *)ioctl_reset }, + { vidioc_int_init_num, + (v4l2_int_ioctl_func *)ioctl_init }, + { vidioc_int_enum_fmt_cap_num, + (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap }, + { vidioc_int_try_fmt_cap_num, + (v4l2_int_ioctl_func *)ioctl_try_fmt_cap }, + { vidioc_int_g_fmt_cap_num, + (v4l2_int_ioctl_func *)ioctl_g_fmt_cap }, + { vidioc_int_s_fmt_cap_num, + (v4l2_int_ioctl_func *)ioctl_s_fmt_cap }, + { vidioc_int_g_parm_num, + (v4l2_int_ioctl_func *)ioctl_g_parm }, + { vidioc_int_s_parm_num, + (v4l2_int_ioctl_func *)ioctl_s_parm }, + { vidioc_int_queryctrl_num, + (v4l2_int_ioctl_func *)ioctl_queryctrl }, + { vidioc_int_g_ctrl_num, + (v4l2_int_ioctl_func *)ioctl_g_ctrl }, + { vidioc_int_s_ctrl_num, + (v4l2_int_ioctl_func *)ioctl_s_ctrl }, +}; + +static struct v4l2_int_slave tcm825x_slave = { + .ioctls = tcm825x_ioctl_desc, + .num_ioctls = ARRAY_SIZE(tcm825x_ioctl_desc), +}; + +static struct tcm825x_sensor tcm825x; + +static struct v4l2_int_device tcm825x_int_device = { + .module = THIS_MODULE, + .name = TCM825X_NAME, + .priv = &tcm825x, + .type = v4l2_int_type_slave, + .u = { + .slave = &tcm825x_slave, + }, +}; + +static int tcm825x_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct tcm825x_sensor *sensor = &tcm825x; + + if (i2c_get_clientdata(client)) + return -EBUSY; + + sensor->platform_data = client->dev.platform_data; + + if (sensor->platform_data == NULL + || !sensor->platform_data->is_okay()) + return -ENODEV; + + sensor->v4l2_int_device = &tcm825x_int_device; + + sensor->i2c_client = client; + i2c_set_clientdata(client, sensor); + + /* Make the default capture format QVGA RGB565 */ + sensor->pix.width = tcm825x_sizes[QVGA].width; + sensor->pix.height = tcm825x_sizes[QVGA].height; + sensor->pix.pixelformat = V4L2_PIX_FMT_RGB565; + + return v4l2_int_device_register(sensor->v4l2_int_device); +} + +static int tcm825x_remove(struct i2c_client *client) +{ + struct tcm825x_sensor *sensor = i2c_get_clientdata(client); + + if (!client->adapter) + return -ENODEV; /* our client isn't attached */ + + v4l2_int_device_unregister(sensor->v4l2_int_device); + + return 0; +} + +static const struct i2c_device_id tcm825x_id[] = { + { "tcm825x", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tcm825x_id); + +static struct i2c_driver tcm825x_i2c_driver = { + .driver = { + .name = TCM825X_NAME, + }, + .probe = tcm825x_probe, + .remove = tcm825x_remove, + .id_table = tcm825x_id, +}; + +static struct tcm825x_sensor tcm825x = { + .timeperframe = { + .numerator = 1, + .denominator = DEFAULT_FPS, + }, +}; + +static int __init tcm825x_init(void) +{ + int rval; + + rval = i2c_add_driver(&tcm825x_i2c_driver); + if (rval) + printk(KERN_INFO "%s: failed registering " TCM825X_NAME "\n", + __func__); + + return rval; +} + +static void __exit tcm825x_exit(void) +{ + i2c_del_driver(&tcm825x_i2c_driver); +} + +/* + * FIXME: Menelaus isn't ready (?) at module_init stage, so use + * late_initcall for now. + */ +late_initcall(tcm825x_init); +module_exit(tcm825x_exit); + +MODULE_AUTHOR("Sakari Ailus "); +MODULE_DESCRIPTION("TCM825x camera sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/tcm825x.h b/drivers/media/i2c/tcm825x.h new file mode 100644 index 00000000000..8ebab953963 --- /dev/null +++ b/drivers/media/i2c/tcm825x.h @@ -0,0 +1,200 @@ +/* + * drivers/media/i2c/tcm825x.h + * + * Register definitions for the TCM825X CameraChip. + * + * Author: David Cohen (david.cohen@indt.org.br) + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + * This file was based on ov9640.h from MontaVista + */ + +#ifndef TCM825X_H +#define TCM825X_H + +#include + +#include + +#define TCM825X_NAME "tcm825x" + +#define TCM825X_MASK(x) x & 0x00ff +#define TCM825X_ADDR(x) (x & 0xff00) >> 8 + +/* The TCM825X I2C sensor chip has a fixed slave address of 0x3d. */ +#define TCM825X_I2C_ADDR 0x3d + +/* + * define register offsets for the TCM825X sensor chip + * OFFSET(8 bits) + MASK(8 bits) + * MASK bit 4 and 3 are used when the register uses more than one address + */ +#define TCM825X_FPS 0x0280 +#define TCM825X_ACF 0x0240 +#define TCM825X_DOUTBUF 0x020C +#define TCM825X_DCLKP 0x0202 +#define TCM825X_ACFDET 0x0201 +#define TCM825X_DOUTSW 0x0380 +#define TCM825X_DATAHZ 0x0340 +#define TCM825X_PICSIZ 0x033c +#define TCM825X_PICFMT 0x0302 +#define TCM825X_V_INV 0x0480 +#define TCM825X_H_INV 0x0440 +#define TCM825X_ESRLSW 0x0430 +#define TCM825X_V_LENGTH 0x040F +#define TCM825X_ALCSW 0x0580 +#define TCM825X_ESRLIM 0x0560 +#define TCM825X_ESRSPD_U 0x051F +#define TCM825X_ESRSPD_L 0x06FF +#define TCM825X_AG 0x07FF +#define TCM825X_ESRSPD2 0x06FF +#define TCM825X_ALCMODE 0x0830 +#define TCM825X_ALCH 0x080F +#define TCM825X_ALCL 0x09FF +#define TCM825X_AWBSW 0x0A80 +#define TCM825X_MRG 0x0BFF +#define TCM825X_MBG 0x0CFF +#define TCM825X_GAMSW 0x0D80 +#define TCM825X_HDTG 0x0EFF +#define TCM825X_VDTG 0x0FFF +#define TCM825X_HDTCORE 0x10F0 +#define TCM825X_VDTCORE 0x100F +#define TCM825X_CONT 0x11FF +#define TCM825X_BRIGHT 0x12FF +#define TCM825X_VHUE 0x137F +#define TCM825X_UHUE 0x147F +#define TCM825X_VGAIN 0x153F +#define TCM825X_UGAIN 0x163F +#define TCM825X_UVCORE 0x170F +#define TCM825X_SATU 0x187F +#define TCM825X_MHMODE 0x1980 +#define TCM825X_MHLPFSEL 0x1940 +#define TCM825X_YMODE 0x1930 +#define TCM825X_MIXHG 0x1907 +#define TCM825X_LENS 0x1A3F +#define TCM825X_AGLIM 0x1BE0 +#define TCM825X_LENSRPOL 0x1B10 +#define TCM825X_LENSRGAIN 0x1B0F +#define TCM825X_ES100S 0x1CFF +#define TCM825X_ES120S 0x1DFF +#define TCM825X_DMASK 0x1EC0 +#define TCM825X_CODESW 0x1E20 +#define TCM825X_CODESEL 0x1E10 +#define TCM825X_TESPIC 0x1E04 +#define TCM825X_PICSEL 0x1E03 +#define TCM825X_HNUM 0x20FF +#define TCM825X_VOUTPH 0x287F +#define TCM825X_ESROUT 0x327F +#define TCM825X_ESROUT2 0x33FF +#define TCM825X_AGOUT 0x34FF +#define TCM825X_DGOUT 0x353F +#define TCM825X_AGSLOW1 0x39C0 +#define TCM825X_FLLSMODE 0x3930 +#define TCM825X_FLLSLIM 0x390F +#define TCM825X_DETSEL 0x3AF0 +#define TCM825X_ACDETNC 0x3A0F +#define TCM825X_AGSLOW2 0x3BC0 +#define TCM825X_DG 0x3B3F +#define TCM825X_REJHLEV 0x3CFF +#define TCM825X_ALCLOCK 0x3D80 +#define TCM825X_FPSLNKSW 0x3D40 +#define TCM825X_ALCSPD 0x3D30 +#define TCM825X_REJH 0x3D03 +#define TCM825X_SHESRSW 0x3E80 +#define TCM825X_ESLIMSEL 0x3E40 +#define TCM825X_SHESRSPD 0x3E30 +#define TCM825X_ELSTEP 0x3E0C +#define TCM825X_ELSTART 0x3E03 +#define TCM825X_AGMIN 0x3FFF +#define TCM825X_PREGRG 0x423F +#define TCM825X_PREGBG 0x433F +#define TCM825X_PRERG 0x443F +#define TCM825X_PREBG 0x453F +#define TCM825X_MSKBR 0x477F +#define TCM825X_MSKGR 0x487F +#define TCM825X_MSKRB 0x497F +#define TCM825X_MSKGB 0x4A7F +#define TCM825X_MSKRG 0x4B7F +#define TCM825X_MSKBG 0x4C7F +#define TCM825X_HDTCSW 0x4D80 +#define TCM825X_VDTCSW 0x4D40 +#define TCM825X_DTCYL 0x4D3F +#define TCM825X_HDTPSW 0x4E80 +#define TCM825X_VDTPSW 0x4E40 +#define TCM825X_DTCGAIN 0x4E3F +#define TCM825X_DTLLIMSW 0x4F10 +#define TCM825X_DTLYLIM 0x4F0F +#define TCM825X_YLCUTLMSK 0x5080 +#define TCM825X_YLCUTL 0x503F +#define TCM825X_YLCUTHMSK 0x5180 +#define TCM825X_YLCUTH 0x513F +#define TCM825X_UVSKNC 0x527F +#define TCM825X_UVLJ 0x537F +#define TCM825X_WBGMIN 0x54FF +#define TCM825X_WBGMAX 0x55FF +#define TCM825X_WBSPDUP 0x5603 +#define TCM825X_ALLAREA 0x5820 +#define TCM825X_WBLOCK 0x5810 +#define TCM825X_WB2SP 0x580F +#define TCM825X_KIZUSW 0x5920 +#define TCM825X_PBRSW 0x5910 +#define TCM825X_ABCSW 0x5903 +#define TCM825X_PBDLV 0x5AFF +#define TCM825X_PBC1LV 0x5BFF + +#define TCM825X_NUM_REGS (TCM825X_ADDR(TCM825X_PBC1LV) + 1) + +#define TCM825X_BYTES_PER_PIXEL 2 + +#define TCM825X_REG_TERM 0xff /* terminating list entry for reg */ +#define TCM825X_VAL_TERM 0xff /* terminating list entry for val */ + +/* define a structure for tcm825x register initialization values */ +struct tcm825x_reg { + u8 val; + u16 reg; +}; + +enum image_size { subQCIF = 0, QQVGA, QCIF, QVGA, CIF, VGA }; +enum pixel_format { YUV422 = 0, RGB565 }; +#define NUM_IMAGE_SIZES 6 +#define NUM_PIXEL_FORMATS 2 + +#define TCM825X_XCLK_MIN 11900000 +#define TCM825X_XCLK_MAX 25000000 + +struct capture_size { + unsigned long width; + unsigned long height; +}; + +struct tcm825x_platform_data { + /* Is the sensor usable? Doesn't yet mean it's there, but you + * can try! */ + int (*is_okay)(void); + /* Set power state, zero is off, non-zero is on. */ + int (*power_set)(int power); + /* Default registers written after power-on or reset. */ + const struct tcm825x_reg *(*default_regs)(void); + int (*needs_reset)(struct v4l2_int_device *s, void *buf, + struct v4l2_pix_format *fmt); + int (*ifparm)(struct v4l2_ifparm *p); + int (*is_upside_down)(void); +}; + +/* Array of image sizes supported by TCM825X. These must be ordered from + * smallest image size to largest. + */ +static const struct capture_size tcm825x_sizes[] = { + { 128, 96 }, /* subQCIF */ + { 160, 120 }, /* QQVGA */ + { 176, 144 }, /* QCIF */ + { 320, 240 }, /* QVGA */ + { 352, 288 }, /* CIF */ + { 640, 480 }, /* VGA */ +}; + +#endif /* ifndef TCM825X_H */ diff --git a/drivers/media/i2c/tda7432.c b/drivers/media/i2c/tda7432.c new file mode 100644 index 00000000000..f7707e65761 --- /dev/null +++ b/drivers/media/i2c/tda7432.c @@ -0,0 +1,485 @@ +/* + * For the STS-Thompson TDA7432 audio processor chip + * + * Handles audio functions: volume, balance, tone, loudness + * This driver will not complain if used with any + * other i2c device with the same address. + * + * Muting and tone control by Jonathan Isom + * + * Copyright (c) 2000 Eric Sandeen + * Copyright (c) 2006 Mauro Carvalho Chehab + * This code is placed under the terms of the GNU General Public License + * Based on tda9855.c by Steve VanDeBogart (vandebo@uclink.berkeley.edu) + * Which was based on tda8425.c by Greg Alexander (c) 1998 + * + * OPTIONS: + * debug - set to 1 if you'd like to see debug messages + * set to 2 if you'd like to be inundated with debug messages + * + * loudness - set between 0 and 15 for varying degrees of loudness effect + * + * maxvol - set maximium volume to +20db (1), default is 0db(0) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifndef VIDEO_AUDIO_BALANCE +# define VIDEO_AUDIO_BALANCE 32 +#endif + +MODULE_AUTHOR("Eric Sandeen "); +MODULE_DESCRIPTION("bttv driver for the tda7432 audio processor chip"); +MODULE_LICENSE("GPL"); + +static int maxvol; +static int loudness; /* disable loudness by default */ +static int debug; /* insmod parameter */ +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Set debugging level from 0 to 3. Default is off(0)."); +module_param(loudness, int, S_IRUGO); +MODULE_PARM_DESC(loudness, "Turn loudness on(1) else off(0). Default is off(0)."); +module_param(maxvol, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(maxvol, "Set maximium volume to +20dB(0) else +0dB(1). Default is +20dB(0)."); + + +/* Structure of address and subaddresses for the tda7432 */ + +struct tda7432 { + struct v4l2_subdev sd; + int addr; + int input; + int volume; + int muted; + int bass, treble; + int lf, lr, rf, rr; + int loud; +}; + +static inline struct tda7432 *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tda7432, sd); +} + +/* The TDA7432 is made by STS-Thompson + * http://www.st.com + * http://us.st.com/stonline/books/pdf/docs/4056.pdf + * + * TDA7432: I2C-bus controlled basic audio processor + * + * The TDA7432 controls basic audio functions like volume, balance, + * and tone control (including loudness). It also has four channel + * output (for front and rear). Since most vidcap cards probably + * don't have 4 channel output, this driver will set front & rear + * together (no independent control). + */ + + /* Subaddresses for TDA7432 */ + +#define TDA7432_IN 0x00 /* Input select */ +#define TDA7432_VL 0x01 /* Volume */ +#define TDA7432_TN 0x02 /* Bass, Treble (Tone) */ +#define TDA7432_LF 0x03 /* Attenuation LF (Left Front) */ +#define TDA7432_LR 0x04 /* Attenuation LR (Left Rear) */ +#define TDA7432_RF 0x05 /* Attenuation RF (Right Front) */ +#define TDA7432_RR 0x06 /* Attenuation RR (Right Rear) */ +#define TDA7432_LD 0x07 /* Loudness */ + + + /* Masks for bits in TDA7432 subaddresses */ + +/* Many of these not used - just for documentation */ + +/* Subaddress 0x00 - Input selection and bass control */ + +/* Bits 0,1,2 control input: + * 0x00 - Stereo input + * 0x02 - Mono input + * 0x03 - Mute (Using Attenuators Plays better with modules) + * Mono probably isn't used - I'm guessing only the stereo + * input is connected on most cards, so we'll set it to stereo. + * + * Bit 3 controls bass cut: 0/1 is non-symmetric/symmetric bass cut + * Bit 4 controls bass range: 0/1 is extended/standard bass range + * + * Highest 3 bits not used + */ + +#define TDA7432_STEREO_IN 0 +#define TDA7432_MONO_IN 2 /* Probably won't be used */ +#define TDA7432_BASS_SYM 1 << 3 +#define TDA7432_BASS_NORM 1 << 4 + +/* Subaddress 0x01 - Volume */ + +/* Lower 7 bits control volume from -79dB to +32dB in 1dB steps + * Recommended maximum is +20 dB + * + * +32dB: 0x00 + * +20dB: 0x0c + * 0dB: 0x20 + * -79dB: 0x6f + * + * MSB (bit 7) controls loudness: 1/0 is loudness on/off + */ + +#define TDA7432_VOL_0DB 0x20 +#define TDA7432_LD_ON 1 << 7 + + +/* Subaddress 0x02 - Tone control */ + +/* Bits 0,1,2 control absolute treble gain from 0dB to 14dB + * 0x0 is 14dB, 0x7 is 0dB + * + * Bit 3 controls treble attenuation/gain (sign) + * 1 = gain (+) + * 0 = attenuation (-) + * + * Bits 4,5,6 control absolute bass gain from 0dB to 14dB + * (This is only true for normal base range, set in 0x00) + * 0x0 << 4 is 14dB, 0x7 is 0dB + * + * Bit 7 controls bass attenuation/gain (sign) + * 1 << 7 = gain (+) + * 0 << 7 = attenuation (-) + * + * Example: + * 1 1 0 1 0 1 0 1 is +4dB bass, -4dB treble + */ + +#define TDA7432_TREBLE_0DB 0xf +#define TDA7432_TREBLE 7 +#define TDA7432_TREBLE_GAIN 1 << 3 +#define TDA7432_BASS_0DB 0xf +#define TDA7432_BASS 7 << 4 +#define TDA7432_BASS_GAIN 1 << 7 + + +/* Subaddress 0x03 - Left Front attenuation */ +/* Subaddress 0x04 - Left Rear attenuation */ +/* Subaddress 0x05 - Right Front attenuation */ +/* Subaddress 0x06 - Right Rear attenuation */ + +/* Bits 0,1,2,3,4 control attenuation from 0dB to -37.5dB + * in 1.5dB steps. + * + * 0x00 is 0dB + * 0x1f is -37.5dB + * + * Bit 5 mutes that channel when set (1 = mute, 0 = unmute) + * We'll use the mute on the input, though (above) + * Bits 6,7 unused + */ + +#define TDA7432_ATTEN_0DB 0x00 +#define TDA7432_MUTE 0x1 << 5 + + +/* Subaddress 0x07 - Loudness Control */ + +/* Bits 0,1,2,3 control loudness from 0dB to -15dB in 1dB steps + * when bit 4 is NOT set + * + * 0x0 is 0dB + * 0xf is -15dB + * + * If bit 4 is set, then there is a flat attenuation according to + * the lower 4 bits, as above. + * + * Bits 5,6,7 unused + */ + + + +/* Begin code */ + +static int tda7432_write(struct v4l2_subdev *sd, int subaddr, int val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + unsigned char buffer[2]; + + v4l2_dbg(2, debug, sd, "In tda7432_write\n"); + v4l2_dbg(1, debug, sd, "Writing %d 0x%x\n", subaddr, val); + buffer[0] = subaddr; + buffer[1] = val; + if (2 != i2c_master_send(client, buffer, 2)) { + v4l2_err(sd, "I/O error, trying (write %d 0x%x)\n", + subaddr, val); + return -1; + } + return 0; +} + +static int tda7432_set(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct tda7432 *t = to_state(sd); + unsigned char buf[16]; + + v4l2_dbg(1, debug, sd, + "tda7432: 7432_set(0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)\n", + t->input, t->volume, t->bass, t->treble, t->lf, t->lr, + t->rf, t->rr, t->loud); + buf[0] = TDA7432_IN; + buf[1] = t->input; + buf[2] = t->volume; + buf[3] = t->bass; + buf[4] = t->treble; + buf[5] = t->lf; + buf[6] = t->lr; + buf[7] = t->rf; + buf[8] = t->rr; + buf[9] = t->loud; + if (10 != i2c_master_send(client, buf, 10)) { + v4l2_err(sd, "I/O error, trying tda7432_set\n"); + return -1; + } + + return 0; +} + +static void do_tda7432_init(struct v4l2_subdev *sd) +{ + struct tda7432 *t = to_state(sd); + + v4l2_dbg(2, debug, sd, "In tda7432_init\n"); + + t->input = TDA7432_STEREO_IN | /* Main (stereo) input */ + TDA7432_BASS_SYM | /* Symmetric bass cut */ + TDA7432_BASS_NORM; /* Normal bass range */ + t->volume = 0x3b ; /* -27dB Volume */ + if (loudness) /* Turn loudness on? */ + t->volume |= TDA7432_LD_ON; + t->muted = 1; + t->treble = TDA7432_TREBLE_0DB; /* 0dB Treble */ + t->bass = TDA7432_BASS_0DB; /* 0dB Bass */ + t->lf = TDA7432_ATTEN_0DB; /* 0dB attenuation */ + t->lr = TDA7432_ATTEN_0DB; /* 0dB attenuation */ + t->rf = TDA7432_ATTEN_0DB; /* 0dB attenuation */ + t->rr = TDA7432_ATTEN_0DB; /* 0dB attenuation */ + t->loud = loudness; /* insmod parameter */ + + tda7432_set(sd); +} + +static int tda7432_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct tda7432 *t = to_state(sd); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value=t->muted; + return 0; + case V4L2_CID_AUDIO_VOLUME: + if (!maxvol){ /* max +20db */ + ctrl->value = ( 0x6f - (t->volume & 0x7F) ) * 630; + } else { /* max 0db */ + ctrl->value = ( 0x6f - (t->volume & 0x7F) ) * 829; + } + return 0; + case V4L2_CID_AUDIO_BALANCE: + { + if ( (t->lf) < (t->rf) ) + /* right is attenuated, balance shifted left */ + ctrl->value = (32768 - 1057*(t->rf)); + else + /* left is attenuated, balance shifted right */ + ctrl->value = (32768 + 1057*(t->lf)); + return 0; + } + case V4L2_CID_AUDIO_BASS: + { + /* Bass/treble 4 bits each */ + int bass=t->bass; + if(bass >= 0x8) + bass = ~(bass - 0x8) & 0xf; + ctrl->value = (bass << 12)+(bass << 8)+(bass << 4)+(bass); + return 0; + } + case V4L2_CID_AUDIO_TREBLE: + { + int treble=t->treble; + if(treble >= 0x8) + treble = ~(treble - 0x8) & 0xf; + ctrl->value = (treble << 12)+(treble << 8)+(treble << 4)+(treble); + return 0; + } + } + return -EINVAL; +} + +static int tda7432_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct tda7432 *t = to_state(sd); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + t->muted=ctrl->value; + break; + case V4L2_CID_AUDIO_VOLUME: + if(!maxvol){ /* max +20db */ + t->volume = 0x6f - ((ctrl->value)/630); + } else { /* max 0db */ + t->volume = 0x6f - ((ctrl->value)/829); + } + if (loudness) /* Turn on the loudness bit */ + t->volume |= TDA7432_LD_ON; + + tda7432_write(sd, TDA7432_VL, t->volume); + return 0; + case V4L2_CID_AUDIO_BALANCE: + if (ctrl->value < 32768) { + /* shifted to left, attenuate right */ + t->rr = (32768 - ctrl->value)/1057; + t->rf = t->rr; + t->lr = TDA7432_ATTEN_0DB; + t->lf = TDA7432_ATTEN_0DB; + } else if(ctrl->value > 32769) { + /* shifted to right, attenuate left */ + t->lf = (ctrl->value - 32768)/1057; + t->lr = t->lf; + t->rr = TDA7432_ATTEN_0DB; + t->rf = TDA7432_ATTEN_0DB; + } else { + /* centered */ + t->rr = TDA7432_ATTEN_0DB; + t->rf = TDA7432_ATTEN_0DB; + t->lf = TDA7432_ATTEN_0DB; + t->lr = TDA7432_ATTEN_0DB; + } + break; + case V4L2_CID_AUDIO_BASS: + t->bass = ctrl->value >> 12; + if(t->bass>= 0x8) + t->bass = (~t->bass & 0xf) + 0x8 ; + + tda7432_write(sd, TDA7432_TN, 0x10 | (t->bass << 4) | t->treble); + return 0; + case V4L2_CID_AUDIO_TREBLE: + t->treble= ctrl->value >> 12; + if(t->treble>= 0x8) + t->treble = (~t->treble & 0xf) + 0x8 ; + + tda7432_write(sd, TDA7432_TN, 0x10 | (t->bass << 4) | t->treble); + return 0; + default: + return -EINVAL; + } + + /* Used for both mute and balance changes */ + if (t->muted) + { + /* Mute & update balance*/ + tda7432_write(sd, TDA7432_LF, t->lf | TDA7432_MUTE); + tda7432_write(sd, TDA7432_LR, t->lr | TDA7432_MUTE); + tda7432_write(sd, TDA7432_RF, t->rf | TDA7432_MUTE); + tda7432_write(sd, TDA7432_RR, t->rr | TDA7432_MUTE); + } else { + tda7432_write(sd, TDA7432_LF, t->lf); + tda7432_write(sd, TDA7432_LR, t->lr); + tda7432_write(sd, TDA7432_RF, t->rf); + tda7432_write(sd, TDA7432_RR, t->rr); + } + return 0; +} + +static int tda7432_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + switch (qc->id) { + case V4L2_CID_AUDIO_VOLUME: + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880); + case V4L2_CID_AUDIO_MUTE: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); + } + return -EINVAL; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tda7432_core_ops = { + .queryctrl = tda7432_queryctrl, + .g_ctrl = tda7432_g_ctrl, + .s_ctrl = tda7432_s_ctrl, +}; + +static const struct v4l2_subdev_ops tda7432_ops = { + .core = &tda7432_core_ops, +}; + +/* ----------------------------------------------------------------------- */ + +/* *********************** * + * i2c interface functions * + * *********************** */ + +static int tda7432_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tda7432 *t; + struct v4l2_subdev *sd; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (!t) + return -ENOMEM; + sd = &t->sd; + v4l2_i2c_subdev_init(sd, client, &tda7432_ops); + if (loudness < 0 || loudness > 15) { + v4l2_warn(sd, "loudness parameter must be between 0 and 15\n"); + if (loudness < 0) + loudness = 0; + if (loudness > 15) + loudness = 15; + } + + do_tda7432_init(sd); + return 0; +} + +static int tda7432_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + do_tda7432_init(sd); + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + return 0; +} + +static const struct i2c_device_id tda7432_id[] = { + { "tda7432", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tda7432_id); + +static struct i2c_driver tda7432_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tda7432", + }, + .probe = tda7432_probe, + .remove = tda7432_remove, + .id_table = tda7432_id, +}; + +module_i2c_driver(tda7432_driver); diff --git a/drivers/media/i2c/tda9840.c b/drivers/media/i2c/tda9840.c new file mode 100644 index 00000000000..3d7ddd93282 --- /dev/null +++ b/drivers/media/i2c/tda9840.c @@ -0,0 +1,224 @@ + /* + tda9840 - i2c-driver for the tda9840 by SGS Thomson + + Copyright (C) 1998-2003 Michael Hunold + Copyright (C) 2008 Hans Verkuil + + The tda9840 is a stereo/dual sound processor with digital + identification. It can be found at address 0x84 on the i2c-bus. + + For detailed informations download the specifications directly + from SGS Thomson at http://www.st.com + + 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 +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Michael Hunold "); +MODULE_DESCRIPTION("tda9840 driver"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0644); + +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +#define SWITCH 0x00 +#define LEVEL_ADJUST 0x02 +#define STEREO_ADJUST 0x03 +#define TEST 0x04 + +#define TDA9840_SET_MUTE 0x00 +#define TDA9840_SET_MONO 0x10 +#define TDA9840_SET_STEREO 0x2a +#define TDA9840_SET_LANG1 0x12 +#define TDA9840_SET_LANG2 0x1e +#define TDA9840_SET_BOTH 0x1a +#define TDA9840_SET_BOTH_R 0x16 +#define TDA9840_SET_EXTERNAL 0x7a + + +static void tda9840_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (i2c_smbus_write_byte_data(client, reg, val)) + v4l2_dbg(1, debug, sd, "error writing %02x to %02x\n", + val, reg); +} + +static int tda9840_status(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 byte; + + if (1 != i2c_master_recv(client, &byte, 1)) { + v4l2_dbg(1, debug, sd, + "i2c_master_recv() failed\n"); + return -EIO; + } + + if (byte & 0x80) { + v4l2_dbg(1, debug, sd, + "TDA9840_DETECT: register contents invalid\n"); + return -EINVAL; + } + + v4l2_dbg(1, debug, sd, "TDA9840_DETECT: byte: 0x%02x\n", byte); + return byte & 0x60; +} + +static int tda9840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) +{ + int stat = tda9840_status(sd); + int byte; + + if (t->index) + return -EINVAL; + + stat = stat < 0 ? 0 : stat; + if (stat == 0 || stat == 0x60) /* mono input */ + byte = TDA9840_SET_MONO; + else if (stat == 0x40) /* stereo input */ + byte = (t->audmode == V4L2_TUNER_MODE_MONO) ? + TDA9840_SET_MONO : TDA9840_SET_STEREO; + else { /* bilingual */ + switch (t->audmode) { + case V4L2_TUNER_MODE_LANG1_LANG2: + byte = TDA9840_SET_BOTH; + break; + case V4L2_TUNER_MODE_LANG2: + byte = TDA9840_SET_LANG2; + break; + default: + byte = TDA9840_SET_LANG1; + break; + } + } + v4l2_dbg(1, debug, sd, "TDA9840_SWITCH: 0x%02x\n", byte); + tda9840_write(sd, SWITCH, byte); + return 0; +} + +static int tda9840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) +{ + int stat = tda9840_status(sd); + + if (stat < 0) + return stat; + + t->rxsubchans = V4L2_TUNER_SUB_MONO; + + switch (stat & 0x60) { + case 0x00: + t->rxsubchans = V4L2_TUNER_SUB_MONO; + break; + case 0x20: + t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + break; + case 0x40: + t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; + break; + default: /* Incorrect detect */ + t->rxsubchans = V4L2_TUNER_MODE_MONO; + break; + } + return 0; +} + +static int tda9840_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TDA9840, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tda9840_core_ops = { + .g_chip_ident = tda9840_g_chip_ident, +}; + +static const struct v4l2_subdev_tuner_ops tda9840_tuner_ops = { + .s_tuner = tda9840_s_tuner, + .g_tuner = tda9840_g_tuner, +}; + +static const struct v4l2_subdev_ops tda9840_ops = { + .core = &tda9840_core_ops, + .tuner = &tda9840_tuner_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static int tda9840_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct v4l2_subdev *sd; + + /* let's see whether this adapter can support what we need */ + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (sd == NULL) + return -ENOMEM; + v4l2_i2c_subdev_init(sd, client, &tda9840_ops); + + /* set initial values for level & stereo - adjustment, mode */ + tda9840_write(sd, LEVEL_ADJUST, 0); + tda9840_write(sd, STEREO_ADJUST, 0); + tda9840_write(sd, SWITCH, TDA9840_SET_STEREO); + return 0; +} + +static int tda9840_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(sd); + return 0; +} + +static const struct i2c_device_id tda9840_id[] = { + { "tda9840", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tda9840_id); + +static struct i2c_driver tda9840_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tda9840", + }, + .probe = tda9840_probe, + .remove = tda9840_remove, + .id_table = tda9840_id, +}; + +module_i2c_driver(tda9840_driver); diff --git a/drivers/media/i2c/tea6415c.c b/drivers/media/i2c/tea6415c.c new file mode 100644 index 00000000000..d1d6ea1dd27 --- /dev/null +++ b/drivers/media/i2c/tea6415c.c @@ -0,0 +1,187 @@ + /* + tea6415c - i2c-driver for the tea6415c by SGS Thomson + + Copyright (C) 1998-2003 Michael Hunold + Copyright (C) 2008 Hans Verkuil + + The tea6415c is a bus controlled video-matrix-switch + with 8 inputs and 6 outputs. + It is cascadable, i.e. it can be found at the addresses + 0x86 and 0x06 on the i2c-bus. + + For detailed informations download the specifications directly + from SGS Thomson at http://www.st.com + + 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 +#include +#include +#include +#include +#include +#include "tea6415c.h" + +MODULE_AUTHOR("Michael Hunold "); +MODULE_DESCRIPTION("tea6415c driver"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0644); + +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + + +/* makes a connection between the input-pin 'i' and the output-pin 'o' */ +static int tea6415c_s_routing(struct v4l2_subdev *sd, + u32 i, u32 o, u32 config) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 byte = 0; + int ret; + + v4l2_dbg(1, debug, sd, "i=%d, o=%d\n", i, o); + + /* check if the pins are valid */ + if (0 == ((1 == i || 3 == i || 5 == i || 6 == i || 8 == i || 10 == i || 20 == i || 11 == i) + && (18 == o || 17 == o || 16 == o || 15 == o || 14 == o || 13 == o))) + return -EINVAL; + + /* to understand this, have a look at the tea6415c-specs (p.5) */ + switch (o) { + case 18: + byte = 0x00; + break; + case 14: + byte = 0x20; + break; + case 16: + byte = 0x10; + break; + case 17: + byte = 0x08; + break; + case 15: + byte = 0x18; + break; + case 13: + byte = 0x28; + break; + }; + + switch (i) { + case 5: + byte |= 0x00; + break; + case 8: + byte |= 0x04; + break; + case 3: + byte |= 0x02; + break; + case 20: + byte |= 0x06; + break; + case 6: + byte |= 0x01; + break; + case 10: + byte |= 0x05; + break; + case 1: + byte |= 0x03; + break; + case 11: + byte |= 0x07; + break; + }; + + ret = i2c_smbus_write_byte(client, byte); + if (ret) { + v4l2_dbg(1, debug, sd, + "i2c_smbus_write_byte() failed, ret:%d\n", ret); + return -EIO; + } + return ret; +} + +static int tea6415c_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEA6415C, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tea6415c_core_ops = { + .g_chip_ident = tea6415c_g_chip_ident, +}; + +static const struct v4l2_subdev_video_ops tea6415c_video_ops = { + .s_routing = tea6415c_s_routing, +}; + +static const struct v4l2_subdev_ops tea6415c_ops = { + .core = &tea6415c_core_ops, + .video = &tea6415c_video_ops, +}; + +static int tea6415c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct v4l2_subdev *sd; + + /* let's see whether this adapter can support what we need */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE)) + return -EIO; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (sd == NULL) + return -ENOMEM; + v4l2_i2c_subdev_init(sd, client, &tea6415c_ops); + return 0; +} + +static int tea6415c_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(sd); + return 0; +} + +static const struct i2c_device_id tea6415c_id[] = { + { "tea6415c", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tea6415c_id); + +static struct i2c_driver tea6415c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tea6415c", + }, + .probe = tea6415c_probe, + .remove = tea6415c_remove, + .id_table = tea6415c_id, +}; + +module_i2c_driver(tea6415c_driver); diff --git a/drivers/media/i2c/tea6415c.h b/drivers/media/i2c/tea6415c.h new file mode 100644 index 00000000000..3a47d697536 --- /dev/null +++ b/drivers/media/i2c/tea6415c.h @@ -0,0 +1,27 @@ +#ifndef __INCLUDED_TEA6415C__ +#define __INCLUDED_TEA6415C__ + +/* the tea6415c's design is quite brain-dead. although there are + 8 inputs and 6 outputs, these aren't enumerated in any way. because + I don't want to say "connect input pin 20 to output pin 17", I define + a "virtual" pin-order. */ + +/* input pins */ +#define TEA6415C_OUTPUT1 18 +#define TEA6415C_OUTPUT2 14 +#define TEA6415C_OUTPUT3 16 +#define TEA6415C_OUTPUT4 17 +#define TEA6415C_OUTPUT5 13 +#define TEA6415C_OUTPUT6 15 + +/* output pins */ +#define TEA6415C_INPUT1 5 +#define TEA6415C_INPUT2 8 +#define TEA6415C_INPUT3 3 +#define TEA6415C_INPUT4 20 +#define TEA6415C_INPUT5 6 +#define TEA6415C_INPUT6 10 +#define TEA6415C_INPUT7 1 +#define TEA6415C_INPUT8 11 + +#endif diff --git a/drivers/media/i2c/tea6420.c b/drivers/media/i2c/tea6420.c new file mode 100644 index 00000000000..38757217a07 --- /dev/null +++ b/drivers/media/i2c/tea6420.c @@ -0,0 +1,169 @@ + /* + tea6420 - i2c-driver for the tea6420 by SGS Thomson + + Copyright (C) 1998-2003 Michael Hunold + Copyright (C) 2008 Hans Verkuil + + The tea6420 is a bus controlled audio-matrix with 5 stereo inputs, + 4 stereo outputs and gain control for each output. + It is cascadable, i.e. it can be found at the addresses 0x98 + and 0x9a on the i2c-bus. + + For detailed informations download the specifications directly + from SGS Thomson at http://www.st.com + + 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 +#include +#include +#include +#include +#include +#include "tea6420.h" + +MODULE_AUTHOR("Michael Hunold "); +MODULE_DESCRIPTION("tea6420 driver"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0644); + +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + + +/* make a connection between the input 'i' and the output 'o' + with gain 'g' (note: i = 6 means 'mute') */ +static int tea6420_s_routing(struct v4l2_subdev *sd, + u32 i, u32 o, u32 config) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int g = (o >> 4) & 0xf; + u8 byte; + int ret; + + o &= 0xf; + v4l2_dbg(1, debug, sd, "i=%d, o=%d, g=%d\n", i, o, g); + + /* check if the parameters are valid */ + if (i < 1 || i > 6 || o < 1 || o > 4 || g < 0 || g > 6 || g % 2 != 0) + return -EINVAL; + + byte = ((o - 1) << 5); + byte |= (i - 1); + + /* to understand this, have a look at the tea6420-specs (p.5) */ + switch (g) { + case 0: + byte |= (3 << 3); + break; + case 2: + byte |= (2 << 3); + break; + case 4: + byte |= (1 << 3); + break; + case 6: + break; + } + + ret = i2c_smbus_write_byte(client, byte); + if (ret) { + v4l2_dbg(1, debug, sd, + "i2c_smbus_write_byte() failed, ret:%d\n", ret); + return -EIO; + } + return 0; +} + +static int tea6420_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEA6420, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tea6420_core_ops = { + .g_chip_ident = tea6420_g_chip_ident, +}; + +static const struct v4l2_subdev_audio_ops tea6420_audio_ops = { + .s_routing = tea6420_s_routing, +}; + +static const struct v4l2_subdev_ops tea6420_ops = { + .core = &tea6420_core_ops, + .audio = &tea6420_audio_ops, +}; + +static int tea6420_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct v4l2_subdev *sd; + int err, i; + + /* let's see whether this adapter can support what we need */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE)) + return -EIO; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (sd == NULL) + return -ENOMEM; + v4l2_i2c_subdev_init(sd, client, &tea6420_ops); + + /* set initial values: set "mute"-input to all outputs at gain 0 */ + err = 0; + for (i = 1; i < 5; i++) + err += tea6420_s_routing(sd, 6, i, 0); + if (err) { + v4l_dbg(1, debug, client, "could not initialize tea6420\n"); + return -ENODEV; + } + return 0; +} + +static int tea6420_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(sd); + return 0; +} + +static const struct i2c_device_id tea6420_id[] = { + { "tea6420", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tea6420_id); + +static struct i2c_driver tea6420_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tea6420", + }, + .probe = tea6420_probe, + .remove = tea6420_remove, + .id_table = tea6420_id, +}; + +module_i2c_driver(tea6420_driver); diff --git a/drivers/media/i2c/tea6420.h b/drivers/media/i2c/tea6420.h new file mode 100644 index 00000000000..4aa3edb3e19 --- /dev/null +++ b/drivers/media/i2c/tea6420.h @@ -0,0 +1,24 @@ +#ifndef __INCLUDED_TEA6420__ +#define __INCLUDED_TEA6420__ + +/* input pins */ +#define TEA6420_OUTPUT1 1 +#define TEA6420_OUTPUT2 2 +#define TEA6420_OUTPUT3 3 +#define TEA6420_OUTPUT4 4 + +/* output pins */ +#define TEA6420_INPUT1 1 +#define TEA6420_INPUT2 2 +#define TEA6420_INPUT3 3 +#define TEA6420_INPUT4 4 +#define TEA6420_INPUT5 5 +#define TEA6420_INPUT6 6 + +/* gain on the output pins, ORed with the output pin */ +#define TEA6420_GAIN0 0x00 +#define TEA6420_GAIN2 0x20 +#define TEA6420_GAIN4 0x40 +#define TEA6420_GAIN6 0x60 + +#endif diff --git a/drivers/media/i2c/ths7303.c b/drivers/media/i2c/ths7303.c new file mode 100644 index 00000000000..e5c0eedebc5 --- /dev/null +++ b/drivers/media/i2c/ths7303.c @@ -0,0 +1,140 @@ +/* + * ths7303- THS7303 Video Amplifier driver + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +MODULE_DESCRIPTION("TI THS7303 video amplifier driver"); +MODULE_AUTHOR("Chaithrika U S"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level 0-1"); + +/* following function is used to set ths7303 */ +static int ths7303_setvalue(struct v4l2_subdev *sd, v4l2_std_id std) +{ + int err = 0; + u8 val; + struct i2c_client *client; + + client = v4l2_get_subdevdata(sd); + + if (std & (V4L2_STD_ALL & ~V4L2_STD_SECAM)) { + val = 0x02; + v4l2_dbg(1, debug, sd, "setting value for SDTV format\n"); + } else { + val = 0x00; + v4l2_dbg(1, debug, sd, "disabling all channels\n"); + } + + err |= i2c_smbus_write_byte_data(client, 0x01, val); + err |= i2c_smbus_write_byte_data(client, 0x02, val); + err |= i2c_smbus_write_byte_data(client, 0x03, val); + + if (err) + v4l2_err(sd, "write failed\n"); + + return err; +} + +static int ths7303_s_std_output(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + return ths7303_setvalue(sd, norm); +} + +static int ths7303_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_THS7303, 0); +} + +static const struct v4l2_subdev_video_ops ths7303_video_ops = { + .s_std_output = ths7303_s_std_output, +}; + +static const struct v4l2_subdev_core_ops ths7303_core_ops = { + .g_chip_ident = ths7303_g_chip_ident, +}; + +static const struct v4l2_subdev_ops ths7303_ops = { + .core = &ths7303_core_ops, + .video = &ths7303_video_ops, +}; + +static int ths7303_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct v4l2_subdev *sd; + v4l2_std_id std_id = V4L2_STD_NTSC; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (sd == NULL) + return -ENOMEM; + + v4l2_i2c_subdev_init(sd, client, &ths7303_ops); + + return ths7303_setvalue(sd, std_id); +} + +static int ths7303_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(sd); + + return 0; +} + +static const struct i2c_device_id ths7303_id[] = { + {"ths7303", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, ths7303_id); + +static struct i2c_driver ths7303_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "ths7303", + }, + .probe = ths7303_probe, + .remove = ths7303_remove, + .id_table = ths7303_id, +}; + +module_i2c_driver(ths7303_driver); diff --git a/drivers/media/i2c/tlv320aic23b.c b/drivers/media/i2c/tlv320aic23b.c new file mode 100644 index 00000000000..809a75a558e --- /dev/null +++ b/drivers/media/i2c/tlv320aic23b.c @@ -0,0 +1,230 @@ +/* + * tlv320aic23b - driver version 0.0.1 + * + * Copyright (C) 2006 Scott Alfter + * + * Based on wm8775 driver + * + * Copyright (C) 2004 Ulf Eklund + * Copyright (C) 2005 Hans Verkuil + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("tlv320aic23b driver"); +MODULE_AUTHOR("Scott Alfter, Ulf Eklund, Hans Verkuil"); +MODULE_LICENSE("GPL"); + + +/* ----------------------------------------------------------------------- */ + +struct tlv320aic23b_state { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; +}; + +static inline struct tlv320aic23b_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tlv320aic23b_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct tlv320aic23b_state, hdl)->sd; +} + +static int tlv320aic23b_write(struct v4l2_subdev *sd, int reg, u16 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int i; + + if ((reg < 0 || reg > 9) && (reg != 15)) { + v4l2_err(sd, "Invalid register R%d\n", reg); + return -1; + } + + for (i = 0; i < 3; i++) + if (i2c_smbus_write_byte_data(client, + (reg << 1) | (val >> 8), val & 0xff) == 0) + return 0; + v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg); + return -1; +} + +static int tlv320aic23b_s_clock_freq(struct v4l2_subdev *sd, u32 freq) +{ + switch (freq) { + case 32000: /* set sample rate to 32 kHz */ + tlv320aic23b_write(sd, 8, 0x018); + break; + case 44100: /* set sample rate to 44.1 kHz */ + tlv320aic23b_write(sd, 8, 0x022); + break; + case 48000: /* set sample rate to 48 kHz */ + tlv320aic23b_write(sd, 8, 0x000); + break; + default: + return -EINVAL; + } + return 0; +} + +static int tlv320aic23b_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + tlv320aic23b_write(sd, 0, 0x180); /* mute both channels */ + /* set gain on both channels to +3.0 dB */ + if (!ctrl->val) + tlv320aic23b_write(sd, 0, 0x119); + return 0; + } + return -EINVAL; +} + +static int tlv320aic23b_log_status(struct v4l2_subdev *sd) +{ + struct tlv320aic23b_state *state = to_state(sd); + + v4l2_ctrl_handler_log_status(&state->hdl, sd->name); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops tlv320aic23b_ctrl_ops = { + .s_ctrl = tlv320aic23b_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops tlv320aic23b_core_ops = { + .log_status = tlv320aic23b_log_status, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, +}; + +static const struct v4l2_subdev_audio_ops tlv320aic23b_audio_ops = { + .s_clock_freq = tlv320aic23b_s_clock_freq, +}; + +static const struct v4l2_subdev_ops tlv320aic23b_ops = { + .core = &tlv320aic23b_core_ops, + .audio = &tlv320aic23b_audio_ops, +}; + +/* ----------------------------------------------------------------------- */ + +/* i2c implementation */ + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ + +static int tlv320aic23b_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tlv320aic23b_state *state; + struct v4l2_subdev *sd; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct tlv320aic23b_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &tlv320aic23b_ops); + + /* Initialize tlv320aic23b */ + + /* RESET */ + tlv320aic23b_write(sd, 15, 0x000); + /* turn off DAC & mic input */ + tlv320aic23b_write(sd, 6, 0x00A); + /* left-justified, 24-bit, master mode */ + tlv320aic23b_write(sd, 7, 0x049); + /* set gain on both channels to +3.0 dB */ + tlv320aic23b_write(sd, 0, 0x119); + /* set sample rate to 48 kHz */ + tlv320aic23b_write(sd, 8, 0x000); + /* activate digital interface */ + tlv320aic23b_write(sd, 9, 0x001); + + v4l2_ctrl_handler_init(&state->hdl, 1); + v4l2_ctrl_new_std(&state->hdl, &tlv320aic23b_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + sd->ctrl_handler = &state->hdl; + if (state->hdl.error) { + int err = state->hdl.error; + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + v4l2_ctrl_handler_setup(&state->hdl); + return 0; +} + +static int tlv320aic23b_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct tlv320aic23b_state *state = to_state(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id tlv320aic23b_id[] = { + { "tlv320aic23b", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tlv320aic23b_id); + +static struct i2c_driver tlv320aic23b_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tlv320aic23b", + }, + .probe = tlv320aic23b_probe, + .remove = tlv320aic23b_remove, + .id_table = tlv320aic23b_id, +}; + +module_i2c_driver(tlv320aic23b_driver); diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c new file mode 100644 index 00000000000..321b3153df8 --- /dev/null +++ b/drivers/media/i2c/tvaudio.c @@ -0,0 +1,2118 @@ +/* + * Driver for simple i2c audio chips. + * + * Copyright (c) 2000 Gerd Knorr + * based on code by: + * Eric Sandeen (eric_sandeen@bigfoot.com) + * Steve VanDeBogart (vandebo@uclink.berkeley.edu) + * Greg Alexander (galexand@acm.org) + * + * Copyright(c) 2005-2008 Mauro Carvalho Chehab + * - Some cleanups, code fixes, etc + * - Convert it to V4L2 API + * + * This code is placed under the terms of the GNU General Public License + * + * OPTIONS: + * debug - set to 1 if you'd like to see debug messages + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/* ---------------------------------------------------------------------- */ +/* insmod args */ + +static int debug; /* insmod parameter */ +module_param(debug, int, 0644); + +MODULE_DESCRIPTION("device driver for various i2c TV sound decoder / audiomux chips"); +MODULE_AUTHOR("Eric Sandeen, Steve VanDeBogart, Greg Alexander, Gerd Knorr"); +MODULE_LICENSE("GPL"); + +#define UNSET (-1U) + +/* ---------------------------------------------------------------------- */ +/* our structs */ + +#define MAXREGS 256 + +struct CHIPSTATE; +typedef int (*getvalue)(int); +typedef int (*checkit)(struct CHIPSTATE*); +typedef int (*initialize)(struct CHIPSTATE*); +typedef int (*getrxsubchans)(struct CHIPSTATE *); +typedef void (*setaudmode)(struct CHIPSTATE*, int mode); + +/* i2c command */ +typedef struct AUDIOCMD { + int count; /* # of bytes to send */ + unsigned char bytes[MAXREGS+1]; /* addr, data, data, ... */ +} audiocmd; + +/* chip description */ +struct CHIPDESC { + char *name; /* chip name */ + int addr_lo, addr_hi; /* i2c address range */ + int registers; /* # of registers */ + + int *insmodopt; + checkit checkit; + initialize initialize; + int flags; +#define CHIP_HAS_VOLUME 1 +#define CHIP_HAS_BASSTREBLE 2 +#define CHIP_HAS_INPUTSEL 4 +#define CHIP_NEED_CHECKMODE 8 + + /* various i2c command sequences */ + audiocmd init; + + /* which register has which value */ + int leftreg,rightreg,treblereg,bassreg; + + /* initialize with (defaults to 65535/65535/32768/32768 */ + int leftinit,rightinit,trebleinit,bassinit; + + /* functions to convert the values (v4l -> chip) */ + getvalue volfunc,treblefunc,bassfunc; + + /* get/set mode */ + getrxsubchans getrxsubchans; + setaudmode setaudmode; + + /* input switch register + values for v4l inputs */ + int inputreg; + int inputmap[4]; + int inputmute; + int inputmask; +}; + +/* current state of the chip */ +struct CHIPSTATE { + struct v4l2_subdev sd; + + /* chip-specific description - should point to + an entry at CHIPDESC table */ + struct CHIPDESC *desc; + + /* shadow register set */ + audiocmd shadow; + + /* current settings */ + __u16 left, right, treble, bass, muted; + int prevmode; + int radio; + int input; + + /* thread */ + struct task_struct *thread; + struct timer_list wt; + int audmode; +}; + +static inline struct CHIPSTATE *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct CHIPSTATE, sd); +} + + +/* ---------------------------------------------------------------------- */ +/* i2c I/O functions */ + +static int chip_write(struct CHIPSTATE *chip, int subaddr, int val) +{ + struct v4l2_subdev *sd = &chip->sd; + struct i2c_client *c = v4l2_get_subdevdata(sd); + unsigned char buffer[2]; + + if (subaddr < 0) { + v4l2_dbg(1, debug, sd, "chip_write: 0x%x\n", val); + chip->shadow.bytes[1] = val; + buffer[0] = val; + if (1 != i2c_master_send(c, buffer, 1)) { + v4l2_warn(sd, "I/O error (write 0x%x)\n", val); + return -1; + } + } else { + if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) { + v4l2_info(sd, + "Tried to access a non-existent register: %d\n", + subaddr); + return -EINVAL; + } + + v4l2_dbg(1, debug, sd, "chip_write: reg%d=0x%x\n", + subaddr, val); + chip->shadow.bytes[subaddr+1] = val; + buffer[0] = subaddr; + buffer[1] = val; + if (2 != i2c_master_send(c, buffer, 2)) { + v4l2_warn(sd, "I/O error (write reg%d=0x%x)\n", + subaddr, val); + return -1; + } + } + return 0; +} + +static int chip_write_masked(struct CHIPSTATE *chip, + int subaddr, int val, int mask) +{ + struct v4l2_subdev *sd = &chip->sd; + + if (mask != 0) { + if (subaddr < 0) { + val = (chip->shadow.bytes[1] & ~mask) | (val & mask); + } else { + if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) { + v4l2_info(sd, + "Tried to access a non-existent register: %d\n", + subaddr); + return -EINVAL; + } + + val = (chip->shadow.bytes[subaddr+1] & ~mask) | (val & mask); + } + } + return chip_write(chip, subaddr, val); +} + +static int chip_read(struct CHIPSTATE *chip) +{ + struct v4l2_subdev *sd = &chip->sd; + struct i2c_client *c = v4l2_get_subdevdata(sd); + unsigned char buffer; + + if (1 != i2c_master_recv(c, &buffer, 1)) { + v4l2_warn(sd, "I/O error (read)\n"); + return -1; + } + v4l2_dbg(1, debug, sd, "chip_read: 0x%x\n", buffer); + return buffer; +} + +static int chip_read2(struct CHIPSTATE *chip, int subaddr) +{ + struct v4l2_subdev *sd = &chip->sd; + struct i2c_client *c = v4l2_get_subdevdata(sd); + unsigned char write[1]; + unsigned char read[1]; + struct i2c_msg msgs[2] = { + { c->addr, 0, 1, write }, + { c->addr, I2C_M_RD, 1, read } + }; + + write[0] = subaddr; + + if (2 != i2c_transfer(c->adapter, msgs, 2)) { + v4l2_warn(sd, "I/O error (read2)\n"); + return -1; + } + v4l2_dbg(1, debug, sd, "chip_read2: reg%d=0x%x\n", + subaddr, read[0]); + return read[0]; +} + +static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd) +{ + struct v4l2_subdev *sd = &chip->sd; + struct i2c_client *c = v4l2_get_subdevdata(sd); + int i; + + if (0 == cmd->count) + return 0; + + if (cmd->count + cmd->bytes[0] - 1 >= ARRAY_SIZE(chip->shadow.bytes)) { + v4l2_info(sd, + "Tried to access a non-existent register range: %d to %d\n", + cmd->bytes[0] + 1, cmd->bytes[0] + cmd->count - 1); + return -EINVAL; + } + + /* FIXME: it seems that the shadow bytes are wrong bellow !*/ + + /* update our shadow register set; print bytes if (debug > 0) */ + v4l2_dbg(1, debug, sd, "chip_cmd(%s): reg=%d, data:", + name, cmd->bytes[0]); + for (i = 1; i < cmd->count; i++) { + if (debug) + printk(KERN_CONT " 0x%x", cmd->bytes[i]); + chip->shadow.bytes[i+cmd->bytes[0]] = cmd->bytes[i]; + } + if (debug) + printk(KERN_CONT "\n"); + + /* send data to the chip */ + if (cmd->count != i2c_master_send(c, cmd->bytes, cmd->count)) { + v4l2_warn(sd, "I/O error (%s)\n", name); + return -1; + } + return 0; +} + +/* ---------------------------------------------------------------------- */ +/* kernel thread for doing i2c stuff asyncronly + * right now it is used only to check the audio mode (mono/stereo/whatever) + * some time after switching to another TV channel, then turn on stereo + * if available, ... + */ + +static void chip_thread_wake(unsigned long data) +{ + struct CHIPSTATE *chip = (struct CHIPSTATE*)data; + wake_up_process(chip->thread); +} + +static int chip_thread(void *data) +{ + struct CHIPSTATE *chip = data; + struct CHIPDESC *desc = chip->desc; + struct v4l2_subdev *sd = &chip->sd; + int mode, selected; + + v4l2_dbg(1, debug, sd, "thread started\n"); + set_freezable(); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (!kthread_should_stop()) + schedule(); + set_current_state(TASK_RUNNING); + try_to_freeze(); + if (kthread_should_stop()) + break; + v4l2_dbg(1, debug, sd, "thread wakeup\n"); + + /* don't do anything for radio */ + if (chip->radio) + continue; + + /* have a look what's going on */ + mode = desc->getrxsubchans(chip); + if (mode == chip->prevmode) + continue; + + /* chip detected a new audio mode - set it */ + v4l2_dbg(1, debug, sd, "thread checkmode\n"); + + chip->prevmode = mode; + + selected = V4L2_TUNER_MODE_MONO; + switch (chip->audmode) { + case V4L2_TUNER_MODE_MONO: + if (mode & V4L2_TUNER_SUB_LANG1) + selected = V4L2_TUNER_MODE_LANG1; + break; + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1: + if (mode & V4L2_TUNER_SUB_LANG1) + selected = V4L2_TUNER_MODE_LANG1; + else if (mode & V4L2_TUNER_SUB_STEREO) + selected = V4L2_TUNER_MODE_STEREO; + break; + case V4L2_TUNER_MODE_LANG2: + if (mode & V4L2_TUNER_SUB_LANG2) + selected = V4L2_TUNER_MODE_LANG2; + else if (mode & V4L2_TUNER_SUB_STEREO) + selected = V4L2_TUNER_MODE_STEREO; + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + if (mode & V4L2_TUNER_SUB_LANG2) + selected = V4L2_TUNER_MODE_LANG1_LANG2; + else if (mode & V4L2_TUNER_SUB_STEREO) + selected = V4L2_TUNER_MODE_STEREO; + } + desc->setaudmode(chip, selected); + + /* schedule next check */ + mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000)); + } + + v4l2_dbg(1, debug, sd, "thread exiting\n"); + return 0; +} + +/* ---------------------------------------------------------------------- */ +/* audio chip descriptions - defines+functions for tda9840 */ + +#define TDA9840_SW 0x00 +#define TDA9840_LVADJ 0x02 +#define TDA9840_STADJ 0x03 +#define TDA9840_TEST 0x04 + +#define TDA9840_MONO 0x10 +#define TDA9840_STEREO 0x2a +#define TDA9840_DUALA 0x12 +#define TDA9840_DUALB 0x1e +#define TDA9840_DUALAB 0x1a +#define TDA9840_DUALBA 0x16 +#define TDA9840_EXTERNAL 0x7a + +#define TDA9840_DS_DUAL 0x20 /* Dual sound identified */ +#define TDA9840_ST_STEREO 0x40 /* Stereo sound identified */ +#define TDA9840_PONRES 0x80 /* Power-on reset detected if = 1 */ + +#define TDA9840_TEST_INT1SN 0x1 /* Integration time 0.5s when set */ +#define TDA9840_TEST_INTFU 0x02 /* Disables integrator function */ + +static int tda9840_getrxsubchans(struct CHIPSTATE *chip) +{ + struct v4l2_subdev *sd = &chip->sd; + int val, mode; + + val = chip_read(chip); + mode = V4L2_TUNER_SUB_MONO; + if (val & TDA9840_DS_DUAL) + mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + if (val & TDA9840_ST_STEREO) + mode = V4L2_TUNER_SUB_STEREO; + + v4l2_dbg(1, debug, sd, + "tda9840_getrxsubchans(): raw chip read: %d, return: %d\n", + val, mode); + return mode; +} + +static void tda9840_setaudmode(struct CHIPSTATE *chip, int mode) +{ + int update = 1; + int t = chip->shadow.bytes[TDA9840_SW + 1] & ~0x7e; + + switch (mode) { + case V4L2_TUNER_MODE_MONO: + t |= TDA9840_MONO; + break; + case V4L2_TUNER_MODE_STEREO: + t |= TDA9840_STEREO; + break; + case V4L2_TUNER_MODE_LANG1: + t |= TDA9840_DUALA; + break; + case V4L2_TUNER_MODE_LANG2: + t |= TDA9840_DUALB; + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + t |= TDA9840_DUALAB; + break; + default: + update = 0; + } + + if (update) + chip_write(chip, TDA9840_SW, t); +} + +static int tda9840_checkit(struct CHIPSTATE *chip) +{ + int rc; + rc = chip_read(chip); + /* lower 5 bits should be 0 */ + return ((rc & 0x1f) == 0) ? 1 : 0; +} + +/* ---------------------------------------------------------------------- */ +/* audio chip descriptions - defines+functions for tda985x */ + +/* subaddresses for TDA9855 */ +#define TDA9855_VR 0x00 /* Volume, right */ +#define TDA9855_VL 0x01 /* Volume, left */ +#define TDA9855_BA 0x02 /* Bass */ +#define TDA9855_TR 0x03 /* Treble */ +#define TDA9855_SW 0x04 /* Subwoofer - not connected on DTV2000 */ + +/* subaddresses for TDA9850 */ +#define TDA9850_C4 0x04 /* Control 1 for TDA9850 */ + +/* subaddesses for both chips */ +#define TDA985x_C5 0x05 /* Control 2 for TDA9850, Control 1 for TDA9855 */ +#define TDA985x_C6 0x06 /* Control 3 for TDA9850, Control 2 for TDA9855 */ +#define TDA985x_C7 0x07 /* Control 4 for TDA9850, Control 3 for TDA9855 */ +#define TDA985x_A1 0x08 /* Alignment 1 for both chips */ +#define TDA985x_A2 0x09 /* Alignment 2 for both chips */ +#define TDA985x_A3 0x0a /* Alignment 3 for both chips */ + +/* Masks for bits in TDA9855 subaddresses */ +/* 0x00 - VR in TDA9855 */ +/* 0x01 - VL in TDA9855 */ +/* lower 7 bits control gain from -71dB (0x28) to 16dB (0x7f) + * in 1dB steps - mute is 0x27 */ + + +/* 0x02 - BA in TDA9855 */ +/* lower 5 bits control bass gain from -12dB (0x06) to 16.5dB (0x19) + * in .5dB steps - 0 is 0x0E */ + + +/* 0x03 - TR in TDA9855 */ +/* 4 bits << 1 control treble gain from -12dB (0x3) to 12dB (0xb) + * in 3dB steps - 0 is 0x7 */ + +/* Masks for bits in both chips' subaddresses */ +/* 0x04 - SW in TDA9855, C4/Control 1 in TDA9850 */ +/* Unique to TDA9855: */ +/* 4 bits << 2 control subwoofer/surround gain from -14db (0x1) to 14db (0xf) + * in 3dB steps - mute is 0x0 */ + +/* Unique to TDA9850: */ +/* lower 4 bits control stereo noise threshold, over which stereo turns off + * set to values of 0x00 through 0x0f for Ster1 through Ster16 */ + + +/* 0x05 - C5 - Control 1 in TDA9855 , Control 2 in TDA9850*/ +/* Unique to TDA9855: */ +#define TDA9855_MUTE 1<<7 /* GMU, Mute at outputs */ +#define TDA9855_AVL 1<<6 /* AVL, Automatic Volume Level */ +#define TDA9855_LOUD 1<<5 /* Loudness, 1==off */ +#define TDA9855_SUR 1<<3 /* Surround / Subwoofer 1==.5(L-R) 0==.5(L+R) */ + /* Bits 0 to 3 select various combinations + * of line in and line out, only the + * interesting ones are defined */ +#define TDA9855_EXT 1<<2 /* Selects inputs LIR and LIL. Pins 41 & 12 */ +#define TDA9855_INT 0 /* Selects inputs LOR and LOL. (internal) */ + +/* Unique to TDA9850: */ +/* lower 4 bits contol SAP noise threshold, over which SAP turns off + * set to values of 0x00 through 0x0f for SAP1 through SAP16 */ + + +/* 0x06 - C6 - Control 2 in TDA9855, Control 3 in TDA9850 */ +/* Common to TDA9855 and TDA9850: */ +#define TDA985x_SAP 3<<6 /* Selects SAP output, mute if not received */ +#define TDA985x_MONOSAP 2<<6 /* Selects Mono on left, SAP on right */ +#define TDA985x_STEREO 1<<6 /* Selects Stereo ouput, mono if not received */ +#define TDA985x_MONO 0 /* Forces Mono output */ +#define TDA985x_LMU 1<<3 /* Mute (LOR/LOL for 9855, OUTL/OUTR for 9850) */ + +/* Unique to TDA9855: */ +#define TDA9855_TZCM 1<<5 /* If set, don't mute till zero crossing */ +#define TDA9855_VZCM 1<<4 /* If set, don't change volume till zero crossing*/ +#define TDA9855_LINEAR 0 /* Linear Stereo */ +#define TDA9855_PSEUDO 1 /* Pseudo Stereo */ +#define TDA9855_SPAT_30 2 /* Spatial Stereo, 30% anti-phase crosstalk */ +#define TDA9855_SPAT_50 3 /* Spatial Stereo, 52% anti-phase crosstalk */ +#define TDA9855_E_MONO 7 /* Forced mono - mono select elseware, so useless*/ + +/* 0x07 - C7 - Control 3 in TDA9855, Control 4 in TDA9850 */ +/* Common to both TDA9855 and TDA9850: */ +/* lower 4 bits control input gain from -3.5dB (0x0) to 4dB (0xF) + * in .5dB steps - 0dB is 0x7 */ + +/* 0x08, 0x09 - A1 and A2 (read/write) */ +/* Common to both TDA9855 and TDA9850: */ +/* lower 5 bites are wideband and spectral expander alignment + * from 0x00 to 0x1f - nominal at 0x0f and 0x10 (read/write) */ +#define TDA985x_STP 1<<5 /* Stereo Pilot/detect (read-only) */ +#define TDA985x_SAPP 1<<6 /* SAP Pilot/detect (read-only) */ +#define TDA985x_STS 1<<7 /* Stereo trigger 1= <35mV 0= <30mV (write-only)*/ + +/* 0x0a - A3 */ +/* Common to both TDA9855 and TDA9850: */ +/* lower 3 bits control timing current for alignment: -30% (0x0), -20% (0x1), + * -10% (0x2), nominal (0x3), +10% (0x6), +20% (0x5), +30% (0x4) */ +#define TDA985x_ADJ 1<<7 /* Stereo adjust on/off (wideband and spectral */ + +static int tda9855_volume(int val) { return val/0x2e8+0x27; } +static int tda9855_bass(int val) { return val/0xccc+0x06; } +static int tda9855_treble(int val) { return (val/0x1c71+0x3)<<1; } + +static int tda985x_getrxsubchans(struct CHIPSTATE *chip) +{ + int mode, val; + + /* Add mono mode regardless of SAP and stereo */ + /* Allows forced mono */ + mode = V4L2_TUNER_SUB_MONO; + val = chip_read(chip); + if (val & TDA985x_STP) + mode = V4L2_TUNER_SUB_STEREO; + if (val & TDA985x_SAPP) + mode |= V4L2_TUNER_SUB_SAP; + return mode; +} + +static void tda985x_setaudmode(struct CHIPSTATE *chip, int mode) +{ + int update = 1; + int c6 = chip->shadow.bytes[TDA985x_C6+1] & 0x3f; + + switch (mode) { + case V4L2_TUNER_MODE_MONO: + c6 |= TDA985x_MONO; + break; + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1: + c6 |= TDA985x_STEREO; + break; + case V4L2_TUNER_MODE_SAP: + c6 |= TDA985x_SAP; + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + c6 |= TDA985x_MONOSAP; + break; + default: + update = 0; + } + if (update) + chip_write(chip,TDA985x_C6,c6); +} + + +/* ---------------------------------------------------------------------- */ +/* audio chip descriptions - defines+functions for tda9873h */ + +/* Subaddresses for TDA9873H */ + +#define TDA9873_SW 0x00 /* Switching */ +#define TDA9873_AD 0x01 /* Adjust */ +#define TDA9873_PT 0x02 /* Port */ + +/* Subaddress 0x00: Switching Data + * B7..B0: + * + * B1, B0: Input source selection + * 0, 0 internal + * 1, 0 external stereo + * 0, 1 external mono + */ +#define TDA9873_INP_MASK 3 +#define TDA9873_INTERNAL 0 +#define TDA9873_EXT_STEREO 2 +#define TDA9873_EXT_MONO 1 + +/* B3, B2: output signal select + * B4 : transmission mode + * 0, 0, 1 Mono + * 1, 0, 0 Stereo + * 1, 1, 1 Stereo (reversed channel) + * 0, 0, 0 Dual AB + * 0, 0, 1 Dual AA + * 0, 1, 0 Dual BB + * 0, 1, 1 Dual BA + */ + +#define TDA9873_TR_MASK (7 << 2) +#define TDA9873_TR_MONO 4 +#define TDA9873_TR_STEREO 1 << 4 +#define TDA9873_TR_REVERSE ((1 << 3) | (1 << 2)) +#define TDA9873_TR_DUALA 1 << 2 +#define TDA9873_TR_DUALB 1 << 3 +#define TDA9873_TR_DUALAB 0 + +/* output level controls + * B5: output level switch (0 = reduced gain, 1 = normal gain) + * B6: mute (1 = muted) + * B7: auto-mute (1 = auto-mute enabled) + */ + +#define TDA9873_GAIN_NORMAL 1 << 5 +#define TDA9873_MUTE 1 << 6 +#define TDA9873_AUTOMUTE 1 << 7 + +/* Subaddress 0x01: Adjust/standard */ + +/* Lower 4 bits (C3..C0) control stereo adjustment on R channel (-0.6 - +0.7 dB) + * Recommended value is +0 dB + */ + +#define TDA9873_STEREO_ADJ 0x06 /* 0dB gain */ + +/* Bits C6..C4 control FM stantard + * C6, C5, C4 + * 0, 0, 0 B/G (PAL FM) + * 0, 0, 1 M + * 0, 1, 0 D/K(1) + * 0, 1, 1 D/K(2) + * 1, 0, 0 D/K(3) + * 1, 0, 1 I + */ +#define TDA9873_BG 0 +#define TDA9873_M 1 +#define TDA9873_DK1 2 +#define TDA9873_DK2 3 +#define TDA9873_DK3 4 +#define TDA9873_I 5 + +/* C7 controls identification response time (1=fast/0=normal) + */ +#define TDA9873_IDR_NORM 0 +#define TDA9873_IDR_FAST 1 << 7 + + +/* Subaddress 0x02: Port data */ + +/* E1, E0 free programmable ports P1/P2 + 0, 0 both ports low + 0, 1 P1 high + 1, 0 P2 high + 1, 1 both ports high +*/ + +#define TDA9873_PORTS 3 + +/* E2: test port */ +#define TDA9873_TST_PORT 1 << 2 + +/* E5..E3 control mono output channel (together with transmission mode bit B4) + * + * E5 E4 E3 B4 OUTM + * 0 0 0 0 mono + * 0 0 1 0 DUAL B + * 0 1 0 1 mono (from stereo decoder) + */ +#define TDA9873_MOUT_MONO 0 +#define TDA9873_MOUT_FMONO 0 +#define TDA9873_MOUT_DUALA 0 +#define TDA9873_MOUT_DUALB 1 << 3 +#define TDA9873_MOUT_ST 1 << 4 +#define TDA9873_MOUT_EXTM ((1 << 4) | (1 << 3)) +#define TDA9873_MOUT_EXTL 1 << 5 +#define TDA9873_MOUT_EXTR ((1 << 5) | (1 << 3)) +#define TDA9873_MOUT_EXTLR ((1 << 5) | (1 << 4)) +#define TDA9873_MOUT_MUTE ((1 << 5) | (1 << 4) | (1 << 3)) + +/* Status bits: (chip read) */ +#define TDA9873_PONR 0 /* Power-on reset detected if = 1 */ +#define TDA9873_STEREO 2 /* Stereo sound is identified */ +#define TDA9873_DUAL 4 /* Dual sound is identified */ + +static int tda9873_getrxsubchans(struct CHIPSTATE *chip) +{ + struct v4l2_subdev *sd = &chip->sd; + int val,mode; + + val = chip_read(chip); + mode = V4L2_TUNER_SUB_MONO; + if (val & TDA9873_STEREO) + mode = V4L2_TUNER_SUB_STEREO; + if (val & TDA9873_DUAL) + mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + v4l2_dbg(1, debug, sd, + "tda9873_getrxsubchans(): raw chip read: %d, return: %d\n", + val, mode); + return mode; +} + +static void tda9873_setaudmode(struct CHIPSTATE *chip, int mode) +{ + struct v4l2_subdev *sd = &chip->sd; + int sw_data = chip->shadow.bytes[TDA9873_SW+1] & ~ TDA9873_TR_MASK; + /* int adj_data = chip->shadow.bytes[TDA9873_AD+1] ; */ + + if ((sw_data & TDA9873_INP_MASK) != TDA9873_INTERNAL) { + v4l2_dbg(1, debug, sd, + "tda9873_setaudmode(): external input\n"); + return; + } + + v4l2_dbg(1, debug, sd, + "tda9873_setaudmode(): chip->shadow.bytes[%d] = %d\n", + TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]); + v4l2_dbg(1, debug, sd, "tda9873_setaudmode(): sw_data = %d\n", + sw_data); + + switch (mode) { + case V4L2_TUNER_MODE_MONO: + sw_data |= TDA9873_TR_MONO; + break; + case V4L2_TUNER_MODE_STEREO: + sw_data |= TDA9873_TR_STEREO; + break; + case V4L2_TUNER_MODE_LANG1: + sw_data |= TDA9873_TR_DUALA; + break; + case V4L2_TUNER_MODE_LANG2: + sw_data |= TDA9873_TR_DUALB; + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + sw_data |= TDA9873_TR_DUALAB; + break; + default: + return; + } + + chip_write(chip, TDA9873_SW, sw_data); + v4l2_dbg(1, debug, sd, + "tda9873_setaudmode(): req. mode %d; chip_write: %d\n", + mode, sw_data); +} + +static int tda9873_checkit(struct CHIPSTATE *chip) +{ + int rc; + + if (-1 == (rc = chip_read2(chip,254))) + return 0; + return (rc & ~0x1f) == 0x80; +} + + +/* ---------------------------------------------------------------------- */ +/* audio chip description - defines+functions for tda9874h and tda9874a */ +/* Dariusz Kowalewski */ + +/* Subaddresses for TDA9874H and TDA9874A (slave rx) */ +#define TDA9874A_AGCGR 0x00 /* AGC gain */ +#define TDA9874A_GCONR 0x01 /* general config */ +#define TDA9874A_MSR 0x02 /* monitor select */ +#define TDA9874A_C1FRA 0x03 /* carrier 1 freq. */ +#define TDA9874A_C1FRB 0x04 /* carrier 1 freq. */ +#define TDA9874A_C1FRC 0x05 /* carrier 1 freq. */ +#define TDA9874A_C2FRA 0x06 /* carrier 2 freq. */ +#define TDA9874A_C2FRB 0x07 /* carrier 2 freq. */ +#define TDA9874A_C2FRC 0x08 /* carrier 2 freq. */ +#define TDA9874A_DCR 0x09 /* demodulator config */ +#define TDA9874A_FMER 0x0a /* FM de-emphasis */ +#define TDA9874A_FMMR 0x0b /* FM dematrix */ +#define TDA9874A_C1OLAR 0x0c /* ch.1 output level adj. */ +#define TDA9874A_C2OLAR 0x0d /* ch.2 output level adj. */ +#define TDA9874A_NCONR 0x0e /* NICAM config */ +#define TDA9874A_NOLAR 0x0f /* NICAM output level adj. */ +#define TDA9874A_NLELR 0x10 /* NICAM lower error limit */ +#define TDA9874A_NUELR 0x11 /* NICAM upper error limit */ +#define TDA9874A_AMCONR 0x12 /* audio mute control */ +#define TDA9874A_SDACOSR 0x13 /* stereo DAC output select */ +#define TDA9874A_AOSR 0x14 /* analog output select */ +#define TDA9874A_DAICONR 0x15 /* digital audio interface config */ +#define TDA9874A_I2SOSR 0x16 /* I2S-bus output select */ +#define TDA9874A_I2SOLAR 0x17 /* I2S-bus output level adj. */ +#define TDA9874A_MDACOSR 0x18 /* mono DAC output select (tda9874a) */ +#define TDA9874A_ESP 0xFF /* easy standard progr. (tda9874a) */ + +/* Subaddresses for TDA9874H and TDA9874A (slave tx) */ +#define TDA9874A_DSR 0x00 /* device status */ +#define TDA9874A_NSR 0x01 /* NICAM status */ +#define TDA9874A_NECR 0x02 /* NICAM error count */ +#define TDA9874A_DR1 0x03 /* add. data LSB */ +#define TDA9874A_DR2 0x04 /* add. data MSB */ +#define TDA9874A_LLRA 0x05 /* monitor level read-out LSB */ +#define TDA9874A_LLRB 0x06 /* monitor level read-out MSB */ +#define TDA9874A_SIFLR 0x07 /* SIF level */ +#define TDA9874A_TR2 252 /* test reg. 2 */ +#define TDA9874A_TR1 253 /* test reg. 1 */ +#define TDA9874A_DIC 254 /* device id. code */ +#define TDA9874A_SIC 255 /* software id. code */ + + +static int tda9874a_mode = 1; /* 0: A2, 1: NICAM */ +static int tda9874a_GCONR = 0xc0; /* default config. input pin: SIFSEL=0 */ +static int tda9874a_NCONR = 0x01; /* default NICAM config.: AMSEL=0,AMUTE=1 */ +static int tda9874a_ESP = 0x07; /* default standard: NICAM D/K */ +static int tda9874a_dic = -1; /* device id. code */ + +/* insmod options for tda9874a */ +static unsigned int tda9874a_SIF = UNSET; +static unsigned int tda9874a_AMSEL = UNSET; +static unsigned int tda9874a_STD = UNSET; +module_param(tda9874a_SIF, int, 0444); +module_param(tda9874a_AMSEL, int, 0444); +module_param(tda9874a_STD, int, 0444); + +/* + * initialization table for tda9874 decoder: + * - carrier 1 freq. registers (3 bytes) + * - carrier 2 freq. registers (3 bytes) + * - demudulator config register + * - FM de-emphasis register (slow identification mode) + * Note: frequency registers must be written in single i2c transfer. + */ +static struct tda9874a_MODES { + char *name; + audiocmd cmd; +} tda9874a_modelist[9] = { + { "A2, B/G", /* default */ + { 9, { TDA9874A_C1FRA, 0x72,0x95,0x55, 0x77,0xA0,0x00, 0x00,0x00 }} }, + { "A2, M (Korea)", + { 9, { TDA9874A_C1FRA, 0x5D,0xC0,0x00, 0x62,0x6A,0xAA, 0x20,0x22 }} }, + { "A2, D/K (1)", + { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x82,0x60,0x00, 0x00,0x00 }} }, + { "A2, D/K (2)", + { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x8C,0x75,0x55, 0x00,0x00 }} }, + { "A2, D/K (3)", + { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x77,0xA0,0x00, 0x00,0x00 }} }, + { "NICAM, I", + { 9, { TDA9874A_C1FRA, 0x7D,0x00,0x00, 0x88,0x8A,0xAA, 0x08,0x33 }} }, + { "NICAM, B/G", + { 9, { TDA9874A_C1FRA, 0x72,0x95,0x55, 0x79,0xEA,0xAA, 0x08,0x33 }} }, + { "NICAM, D/K", + { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x79,0xEA,0xAA, 0x08,0x33 }} }, + { "NICAM, L", + { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x79,0xEA,0xAA, 0x09,0x33 }} } +}; + +static int tda9874a_setup(struct CHIPSTATE *chip) +{ + struct v4l2_subdev *sd = &chip->sd; + + chip_write(chip, TDA9874A_AGCGR, 0x00); /* 0 dB */ + chip_write(chip, TDA9874A_GCONR, tda9874a_GCONR); + chip_write(chip, TDA9874A_MSR, (tda9874a_mode) ? 0x03:0x02); + if(tda9874a_dic == 0x11) { + chip_write(chip, TDA9874A_FMMR, 0x80); + } else { /* dic == 0x07 */ + chip_cmd(chip,"tda9874_modelist",&tda9874a_modelist[tda9874a_STD].cmd); + chip_write(chip, TDA9874A_FMMR, 0x00); + } + chip_write(chip, TDA9874A_C1OLAR, 0x00); /* 0 dB */ + chip_write(chip, TDA9874A_C2OLAR, 0x00); /* 0 dB */ + chip_write(chip, TDA9874A_NCONR, tda9874a_NCONR); + chip_write(chip, TDA9874A_NOLAR, 0x00); /* 0 dB */ + /* Note: If signal quality is poor you may want to change NICAM */ + /* error limit registers (NLELR and NUELR) to some greater values. */ + /* Then the sound would remain stereo, but won't be so clear. */ + chip_write(chip, TDA9874A_NLELR, 0x14); /* default */ + chip_write(chip, TDA9874A_NUELR, 0x50); /* default */ + + if(tda9874a_dic == 0x11) { + chip_write(chip, TDA9874A_AMCONR, 0xf9); + chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80); + chip_write(chip, TDA9874A_AOSR, 0x80); + chip_write(chip, TDA9874A_MDACOSR, (tda9874a_mode) ? 0x82:0x80); + chip_write(chip, TDA9874A_ESP, tda9874a_ESP); + } else { /* dic == 0x07 */ + chip_write(chip, TDA9874A_AMCONR, 0xfb); + chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80); + chip_write(chip, TDA9874A_AOSR, 0x00); /* or 0x10 */ + } + v4l2_dbg(1, debug, sd, "tda9874a_setup(): %s [0x%02X].\n", + tda9874a_modelist[tda9874a_STD].name,tda9874a_STD); + return 1; +} + +static int tda9874a_getrxsubchans(struct CHIPSTATE *chip) +{ + struct v4l2_subdev *sd = &chip->sd; + int dsr,nsr,mode; + int necr; /* just for debugging */ + + mode = V4L2_TUNER_SUB_MONO; + + if(-1 == (dsr = chip_read2(chip,TDA9874A_DSR))) + return mode; + if(-1 == (nsr = chip_read2(chip,TDA9874A_NSR))) + return mode; + if(-1 == (necr = chip_read2(chip,TDA9874A_NECR))) + return mode; + + /* need to store dsr/nsr somewhere */ + chip->shadow.bytes[MAXREGS-2] = dsr; + chip->shadow.bytes[MAXREGS-1] = nsr; + + if(tda9874a_mode) { + /* Note: DSR.RSSF and DSR.AMSTAT bits are also checked. + * If NICAM auto-muting is enabled, DSR.AMSTAT=1 indicates + * that sound has (temporarily) switched from NICAM to + * mono FM (or AM) on 1st sound carrier due to high NICAM bit + * error count. So in fact there is no stereo in this case :-( + * But changing the mode to V4L2_TUNER_MODE_MONO would switch + * external 4052 multiplexer in audio_hook(). + */ + if(nsr & 0x02) /* NSR.S/MB=1 */ + mode = V4L2_TUNER_SUB_STEREO; + if(nsr & 0x01) /* NSR.D/SB=1 */ + mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + } else { + if(dsr & 0x02) /* DSR.IDSTE=1 */ + mode = V4L2_TUNER_SUB_STEREO; + if(dsr & 0x04) /* DSR.IDDUA=1 */ + mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + } + + v4l2_dbg(1, debug, sd, + "tda9874a_getrxsubchans(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n", + dsr, nsr, necr, mode); + return mode; +} + +static void tda9874a_setaudmode(struct CHIPSTATE *chip, int mode) +{ + struct v4l2_subdev *sd = &chip->sd; + + /* Disable/enable NICAM auto-muting (based on DSR.RSSF status bit). */ + /* If auto-muting is disabled, we can hear a signal of degrading quality. */ + if (tda9874a_mode) { + if(chip->shadow.bytes[MAXREGS-2] & 0x20) /* DSR.RSSF=1 */ + tda9874a_NCONR &= 0xfe; /* enable */ + else + tda9874a_NCONR |= 0x01; /* disable */ + chip_write(chip, TDA9874A_NCONR, tda9874a_NCONR); + } + + /* Note: TDA9874A supports automatic FM dematrixing (FMMR register) + * and has auto-select function for audio output (AOSR register). + * Old TDA9874H doesn't support these features. + * TDA9874A also has additional mono output pin (OUTM), which + * on same (all?) tv-cards is not used, anyway (as well as MONOIN). + */ + if(tda9874a_dic == 0x11) { + int aosr = 0x80; + int mdacosr = (tda9874a_mode) ? 0x82:0x80; + + switch(mode) { + case V4L2_TUNER_MODE_MONO: + case V4L2_TUNER_MODE_STEREO: + break; + case V4L2_TUNER_MODE_LANG1: + aosr = 0x80; /* auto-select, dual A/A */ + mdacosr = (tda9874a_mode) ? 0x82:0x80; + break; + case V4L2_TUNER_MODE_LANG2: + aosr = 0xa0; /* auto-select, dual B/B */ + mdacosr = (tda9874a_mode) ? 0x83:0x81; + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + aosr = 0x00; /* always route L to L and R to R */ + mdacosr = (tda9874a_mode) ? 0x82:0x80; + break; + default: + return; + } + chip_write(chip, TDA9874A_AOSR, aosr); + chip_write(chip, TDA9874A_MDACOSR, mdacosr); + + v4l2_dbg(1, debug, sd, + "tda9874a_setaudmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n", + mode, aosr, mdacosr); + + } else { /* dic == 0x07 */ + int fmmr,aosr; + + switch(mode) { + case V4L2_TUNER_MODE_MONO: + fmmr = 0x00; /* mono */ + aosr = 0x10; /* A/A */ + break; + case V4L2_TUNER_MODE_STEREO: + if(tda9874a_mode) { + fmmr = 0x00; + aosr = 0x00; /* handled by NICAM auto-mute */ + } else { + fmmr = (tda9874a_ESP == 1) ? 0x05 : 0x04; /* stereo */ + aosr = 0x00; + } + break; + case V4L2_TUNER_MODE_LANG1: + fmmr = 0x02; /* dual */ + aosr = 0x10; /* dual A/A */ + break; + case V4L2_TUNER_MODE_LANG2: + fmmr = 0x02; /* dual */ + aosr = 0x20; /* dual B/B */ + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + fmmr = 0x02; /* dual */ + aosr = 0x00; /* dual A/B */ + break; + default: + return; + } + chip_write(chip, TDA9874A_FMMR, fmmr); + chip_write(chip, TDA9874A_AOSR, aosr); + + v4l2_dbg(1, debug, sd, + "tda9874a_setaudmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n", + mode, fmmr, aosr); + } +} + +static int tda9874a_checkit(struct CHIPSTATE *chip) +{ + struct v4l2_subdev *sd = &chip->sd; + int dic,sic; /* device id. and software id. codes */ + + if(-1 == (dic = chip_read2(chip,TDA9874A_DIC))) + return 0; + if(-1 == (sic = chip_read2(chip,TDA9874A_SIC))) + return 0; + + v4l2_dbg(1, debug, sd, "tda9874a_checkit(): DIC=0x%X, SIC=0x%X.\n", dic, sic); + + if((dic == 0x11)||(dic == 0x07)) { + v4l2_info(sd, "found tda9874%s.\n", (dic == 0x11) ? "a" : "h"); + tda9874a_dic = dic; /* remember device id. */ + return 1; + } + return 0; /* not found */ +} + +static int tda9874a_initialize(struct CHIPSTATE *chip) +{ + if (tda9874a_SIF > 2) + tda9874a_SIF = 1; + if (tda9874a_STD >= ARRAY_SIZE(tda9874a_modelist)) + tda9874a_STD = 0; + if(tda9874a_AMSEL > 1) + tda9874a_AMSEL = 0; + + if(tda9874a_SIF == 1) + tda9874a_GCONR = 0xc0; /* sound IF input 1 */ + else + tda9874a_GCONR = 0xc1; /* sound IF input 2 */ + + tda9874a_ESP = tda9874a_STD; + tda9874a_mode = (tda9874a_STD < 5) ? 0 : 1; + + if(tda9874a_AMSEL == 0) + tda9874a_NCONR = 0x01; /* auto-mute: analog mono input */ + else + tda9874a_NCONR = 0x05; /* auto-mute: 1st carrier FM or AM */ + + tda9874a_setup(chip); + return 0; +} + +/* ---------------------------------------------------------------------- */ +/* audio chip description - defines+functions for tda9875 */ +/* The TDA9875 is made by Philips Semiconductor + * http://www.semiconductors.philips.com + * TDA9875: I2C-bus controlled DSP audio processor, FM demodulator + * + */ + +/* subaddresses for TDA9875 */ +#define TDA9875_MUT 0x12 /*General mute (value --> 0b11001100*/ +#define TDA9875_CFG 0x01 /* Config register (value --> 0b00000000 */ +#define TDA9875_DACOS 0x13 /*DAC i/o select (ADC) 0b0000100*/ +#define TDA9875_LOSR 0x16 /*Line output select regirter 0b0100 0001*/ + +#define TDA9875_CH1V 0x0c /*Channel 1 volume (mute)*/ +#define TDA9875_CH2V 0x0d /*Channel 2 volume (mute)*/ +#define TDA9875_SC1 0x14 /*SCART 1 in (mono)*/ +#define TDA9875_SC2 0x15 /*SCART 2 in (mono)*/ + +#define TDA9875_ADCIS 0x17 /*ADC input select (mono) 0b0110 000*/ +#define TDA9875_AER 0x19 /*Audio effect (AVL+Pseudo) 0b0000 0110*/ +#define TDA9875_MCS 0x18 /*Main channel select (DAC) 0b0000100*/ +#define TDA9875_MVL 0x1a /* Main volume gauche */ +#define TDA9875_MVR 0x1b /* Main volume droite */ +#define TDA9875_MBA 0x1d /* Main Basse */ +#define TDA9875_MTR 0x1e /* Main treble */ +#define TDA9875_ACS 0x1f /* Auxiliary channel select (FM) 0b0000000*/ +#define TDA9875_AVL 0x20 /* Auxiliary volume gauche */ +#define TDA9875_AVR 0x21 /* Auxiliary volume droite */ +#define TDA9875_ABA 0x22 /* Auxiliary Basse */ +#define TDA9875_ATR 0x23 /* Auxiliary treble */ + +#define TDA9875_MSR 0x02 /* Monitor select register */ +#define TDA9875_C1MSB 0x03 /* Carrier 1 (FM) frequency register MSB */ +#define TDA9875_C1MIB 0x04 /* Carrier 1 (FM) frequency register (16-8]b */ +#define TDA9875_C1LSB 0x05 /* Carrier 1 (FM) frequency register LSB */ +#define TDA9875_C2MSB 0x06 /* Carrier 2 (nicam) frequency register MSB */ +#define TDA9875_C2MIB 0x07 /* Carrier 2 (nicam) frequency register (16-8]b */ +#define TDA9875_C2LSB 0x08 /* Carrier 2 (nicam) frequency register LSB */ +#define TDA9875_DCR 0x09 /* Demodulateur configuration regirter*/ +#define TDA9875_DEEM 0x0a /* FM de-emphasis regirter*/ +#define TDA9875_FMAT 0x0b /* FM Matrix regirter*/ + +/* values */ +#define TDA9875_MUTE_ON 0xff /* general mute */ +#define TDA9875_MUTE_OFF 0xcc /* general no mute */ + +static int tda9875_initialize(struct CHIPSTATE *chip) +{ + chip_write(chip, TDA9875_CFG, 0xd0); /*reg de config 0 (reset)*/ + chip_write(chip, TDA9875_MSR, 0x03); /* Monitor 0b00000XXX*/ + chip_write(chip, TDA9875_C1MSB, 0x00); /*Car1(FM) MSB XMHz*/ + chip_write(chip, TDA9875_C1MIB, 0x00); /*Car1(FM) MIB XMHz*/ + chip_write(chip, TDA9875_C1LSB, 0x00); /*Car1(FM) LSB XMHz*/ + chip_write(chip, TDA9875_C2MSB, 0x00); /*Car2(NICAM) MSB XMHz*/ + chip_write(chip, TDA9875_C2MIB, 0x00); /*Car2(NICAM) MIB XMHz*/ + chip_write(chip, TDA9875_C2LSB, 0x00); /*Car2(NICAM) LSB XMHz*/ + chip_write(chip, TDA9875_DCR, 0x00); /*Demod config 0x00*/ + chip_write(chip, TDA9875_DEEM, 0x44); /*DE-Emph 0b0100 0100*/ + chip_write(chip, TDA9875_FMAT, 0x00); /*FM Matrix reg 0x00*/ + chip_write(chip, TDA9875_SC1, 0x00); /* SCART 1 (SC1)*/ + chip_write(chip, TDA9875_SC2, 0x01); /* SCART 2 (sc2)*/ + + chip_write(chip, TDA9875_CH1V, 0x10); /* Channel volume 1 mute*/ + chip_write(chip, TDA9875_CH2V, 0x10); /* Channel volume 2 mute */ + chip_write(chip, TDA9875_DACOS, 0x02); /* sig DAC i/o(in:nicam)*/ + chip_write(chip, TDA9875_ADCIS, 0x6f); /* sig ADC input(in:mono)*/ + chip_write(chip, TDA9875_LOSR, 0x00); /* line out (in:mono)*/ + chip_write(chip, TDA9875_AER, 0x00); /*06 Effect (AVL+PSEUDO) */ + chip_write(chip, TDA9875_MCS, 0x44); /* Main ch select (DAC) */ + chip_write(chip, TDA9875_MVL, 0x03); /* Vol Main left 10dB */ + chip_write(chip, TDA9875_MVR, 0x03); /* Vol Main right 10dB*/ + chip_write(chip, TDA9875_MBA, 0x00); /* Main Bass Main 0dB*/ + chip_write(chip, TDA9875_MTR, 0x00); /* Main Treble Main 0dB*/ + chip_write(chip, TDA9875_ACS, 0x44); /* Aux chan select (dac)*/ + chip_write(chip, TDA9875_AVL, 0x00); /* Vol Aux left 0dB*/ + chip_write(chip, TDA9875_AVR, 0x00); /* Vol Aux right 0dB*/ + chip_write(chip, TDA9875_ABA, 0x00); /* Aux Bass Main 0dB*/ + chip_write(chip, TDA9875_ATR, 0x00); /* Aux Aigus Main 0dB*/ + + chip_write(chip, TDA9875_MUT, 0xcc); /* General mute */ + return 0; +} + +static int tda9875_volume(int val) { return (unsigned char)(val / 602 - 84); } +static int tda9875_bass(int val) { return (unsigned char)(max(-12, val / 2115 - 15)); } +static int tda9875_treble(int val) { return (unsigned char)(val / 2622 - 12); } + +/* ----------------------------------------------------------------------- */ + + +/* *********************** * + * i2c interface functions * + * *********************** */ + +static int tda9875_checkit(struct CHIPSTATE *chip) +{ + struct v4l2_subdev *sd = &chip->sd; + int dic, rev; + + dic = chip_read2(chip, 254); + rev = chip_read2(chip, 255); + + if (dic == 0 || dic == 2) { /* tda9875 and tda9875A */ + v4l2_info(sd, "found tda9875%s rev. %d.\n", + dic == 0 ? "" : "A", rev); + return 1; + } + return 0; +} + +/* ---------------------------------------------------------------------- */ +/* audio chip descriptions - defines+functions for tea6420 */ + +#define TEA6300_VL 0x00 /* volume left */ +#define TEA6300_VR 0x01 /* volume right */ +#define TEA6300_BA 0x02 /* bass */ +#define TEA6300_TR 0x03 /* treble */ +#define TEA6300_FA 0x04 /* fader control */ +#define TEA6300_S 0x05 /* switch register */ + /* values for those registers: */ +#define TEA6300_S_SA 0x01 /* stereo A input */ +#define TEA6300_S_SB 0x02 /* stereo B */ +#define TEA6300_S_SC 0x04 /* stereo C */ +#define TEA6300_S_GMU 0x80 /* general mute */ + +#define TEA6320_V 0x00 /* volume (0-5)/loudness off (6)/zero crossing mute(7) */ +#define TEA6320_FFR 0x01 /* fader front right (0-5) */ +#define TEA6320_FFL 0x02 /* fader front left (0-5) */ +#define TEA6320_FRR 0x03 /* fader rear right (0-5) */ +#define TEA6320_FRL 0x04 /* fader rear left (0-5) */ +#define TEA6320_BA 0x05 /* bass (0-4) */ +#define TEA6320_TR 0x06 /* treble (0-4) */ +#define TEA6320_S 0x07 /* switch register */ + /* values for those registers: */ +#define TEA6320_S_SA 0x07 /* stereo A input */ +#define TEA6320_S_SB 0x06 /* stereo B */ +#define TEA6320_S_SC 0x05 /* stereo C */ +#define TEA6320_S_SD 0x04 /* stereo D */ +#define TEA6320_S_GMU 0x80 /* general mute */ + +#define TEA6420_S_SA 0x00 /* stereo A input */ +#define TEA6420_S_SB 0x01 /* stereo B */ +#define TEA6420_S_SC 0x02 /* stereo C */ +#define TEA6420_S_SD 0x03 /* stereo D */ +#define TEA6420_S_SE 0x04 /* stereo E */ +#define TEA6420_S_GMU 0x05 /* general mute */ + +static int tea6300_shift10(int val) { return val >> 10; } +static int tea6300_shift12(int val) { return val >> 12; } + +/* Assumes 16bit input (values 0x3f to 0x0c are unique, values less than */ +/* 0x0c mirror those immediately higher) */ +static int tea6320_volume(int val) { return (val / (65535/(63-12)) + 12) & 0x3f; } +static int tea6320_shift11(int val) { return val >> 11; } +static int tea6320_initialize(struct CHIPSTATE * chip) +{ + chip_write(chip, TEA6320_FFR, 0x3f); + chip_write(chip, TEA6320_FFL, 0x3f); + chip_write(chip, TEA6320_FRR, 0x3f); + chip_write(chip, TEA6320_FRL, 0x3f); + + return 0; +} + + +/* ---------------------------------------------------------------------- */ +/* audio chip descriptions - defines+functions for tda8425 */ + +#define TDA8425_VL 0x00 /* volume left */ +#define TDA8425_VR 0x01 /* volume right */ +#define TDA8425_BA 0x02 /* bass */ +#define TDA8425_TR 0x03 /* treble */ +#define TDA8425_S1 0x08 /* switch functions */ + /* values for those registers: */ +#define TDA8425_S1_OFF 0xEE /* audio off (mute on) */ +#define TDA8425_S1_CH1 0xCE /* audio channel 1 (mute off) - "linear stereo" mode */ +#define TDA8425_S1_CH2 0xCF /* audio channel 2 (mute off) - "linear stereo" mode */ +#define TDA8425_S1_MU 0x20 /* mute bit */ +#define TDA8425_S1_STEREO 0x18 /* stereo bits */ +#define TDA8425_S1_STEREO_SPATIAL 0x18 /* spatial stereo */ +#define TDA8425_S1_STEREO_LINEAR 0x08 /* linear stereo */ +#define TDA8425_S1_STEREO_PSEUDO 0x10 /* pseudo stereo */ +#define TDA8425_S1_STEREO_MONO 0x00 /* forced mono */ +#define TDA8425_S1_ML 0x06 /* language selector */ +#define TDA8425_S1_ML_SOUND_A 0x02 /* sound a */ +#define TDA8425_S1_ML_SOUND_B 0x04 /* sound b */ +#define TDA8425_S1_ML_STEREO 0x06 /* stereo */ +#define TDA8425_S1_IS 0x01 /* channel selector */ + + +static int tda8425_shift10(int val) { return (val >> 10) | 0xc0; } +static int tda8425_shift12(int val) { return (val >> 12) | 0xf0; } + +static void tda8425_setaudmode(struct CHIPSTATE *chip, int mode) +{ + int s1 = chip->shadow.bytes[TDA8425_S1+1] & 0xe1; + + switch (mode) { + case V4L2_TUNER_MODE_LANG1: + s1 |= TDA8425_S1_ML_SOUND_A; + s1 |= TDA8425_S1_STEREO_PSEUDO; + break; + case V4L2_TUNER_MODE_LANG2: + s1 |= TDA8425_S1_ML_SOUND_B; + s1 |= TDA8425_S1_STEREO_PSEUDO; + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + s1 |= TDA8425_S1_ML_STEREO; + s1 |= TDA8425_S1_STEREO_LINEAR; + break; + case V4L2_TUNER_MODE_MONO: + s1 |= TDA8425_S1_ML_STEREO; + s1 |= TDA8425_S1_STEREO_MONO; + break; + case V4L2_TUNER_MODE_STEREO: + s1 |= TDA8425_S1_ML_STEREO; + s1 |= TDA8425_S1_STEREO_SPATIAL; + break; + default: + return; + } + chip_write(chip,TDA8425_S1,s1); +} + + +/* ---------------------------------------------------------------------- */ +/* audio chip descriptions - defines+functions for pic16c54 (PV951) */ + +/* the registers of 16C54, I2C sub address. */ +#define PIC16C54_REG_KEY_CODE 0x01 /* Not use. */ +#define PIC16C54_REG_MISC 0x02 + +/* bit definition of the RESET register, I2C data. */ +#define PIC16C54_MISC_RESET_REMOTE_CTL 0x01 /* bit 0, Reset to receive the key */ + /* code of remote controller */ +#define PIC16C54_MISC_MTS_MAIN 0x02 /* bit 1 */ +#define PIC16C54_MISC_MTS_SAP 0x04 /* bit 2 */ +#define PIC16C54_MISC_MTS_BOTH 0x08 /* bit 3 */ +#define PIC16C54_MISC_SND_MUTE 0x10 /* bit 4, Mute Audio(Line-in and Tuner) */ +#define PIC16C54_MISC_SND_NOTMUTE 0x20 /* bit 5 */ +#define PIC16C54_MISC_SWITCH_TUNER 0x40 /* bit 6 , Switch to Line-in */ +#define PIC16C54_MISC_SWITCH_LINE 0x80 /* bit 7 , Switch to Tuner */ + +/* ---------------------------------------------------------------------- */ +/* audio chip descriptions - defines+functions for TA8874Z */ + +/* write 1st byte */ +#define TA8874Z_LED_STE 0x80 +#define TA8874Z_LED_BIL 0x40 +#define TA8874Z_LED_EXT 0x20 +#define TA8874Z_MONO_SET 0x10 +#define TA8874Z_MUTE 0x08 +#define TA8874Z_F_MONO 0x04 +#define TA8874Z_MODE_SUB 0x02 +#define TA8874Z_MODE_MAIN 0x01 + +/* write 2nd byte */ +/*#define TA8874Z_TI 0x80 */ /* test mode */ +#define TA8874Z_SEPARATION 0x3f +#define TA8874Z_SEPARATION_DEFAULT 0x10 + +/* read */ +#define TA8874Z_B1 0x80 +#define TA8874Z_B0 0x40 +#define TA8874Z_CHAG_FLAG 0x20 + +/* + * B1 B0 + * mono L H + * stereo L L + * BIL H L + */ +static int ta8874z_getrxsubchans(struct CHIPSTATE *chip) +{ + int val, mode; + + val = chip_read(chip); + mode = V4L2_TUNER_SUB_MONO; + if (val & TA8874Z_B1){ + mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + }else if (!(val & TA8874Z_B0)){ + mode = V4L2_TUNER_SUB_STEREO; + } + /* v4l2_dbg(1, debug, &chip->sd, + "ta8874z_getrxsubchans(): raw chip read: 0x%02x, return: 0x%02x\n", + val, mode); */ + return mode; +} + +static audiocmd ta8874z_stereo = { 2, {0, TA8874Z_SEPARATION_DEFAULT}}; +static audiocmd ta8874z_mono = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}}; +static audiocmd ta8874z_main = {2, { 0, TA8874Z_SEPARATION_DEFAULT}}; +static audiocmd ta8874z_sub = {2, { TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT}}; +static audiocmd ta8874z_both = {2, { TA8874Z_MODE_MAIN | TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT}}; + +static void ta8874z_setaudmode(struct CHIPSTATE *chip, int mode) +{ + struct v4l2_subdev *sd = &chip->sd; + int update = 1; + audiocmd *t = NULL; + + v4l2_dbg(1, debug, sd, "ta8874z_setaudmode(): mode: 0x%02x\n", mode); + + switch(mode){ + case V4L2_TUNER_MODE_MONO: + t = &ta8874z_mono; + break; + case V4L2_TUNER_MODE_STEREO: + t = &ta8874z_stereo; + break; + case V4L2_TUNER_MODE_LANG1: + t = &ta8874z_main; + break; + case V4L2_TUNER_MODE_LANG2: + t = &ta8874z_sub; + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + t = &ta8874z_both; + break; + default: + update = 0; + } + + if(update) + chip_cmd(chip, "TA8874Z", t); +} + +static int ta8874z_checkit(struct CHIPSTATE *chip) +{ + int rc; + rc = chip_read(chip); + return ((rc & 0x1f) == 0x1f) ? 1 : 0; +} + +/* ---------------------------------------------------------------------- */ +/* audio chip descriptions - struct CHIPDESC */ + +/* insmod options to enable/disable individual audio chips */ +static int tda8425 = 1; +static int tda9840 = 1; +static int tda9850 = 1; +static int tda9855 = 1; +static int tda9873 = 1; +static int tda9874a = 1; +static int tda9875 = 1; +static int tea6300; /* default 0 - address clash with msp34xx */ +static int tea6320; /* default 0 - address clash with msp34xx */ +static int tea6420 = 1; +static int pic16c54 = 1; +static int ta8874z; /* default 0 - address clash with tda9840 */ + +module_param(tda8425, int, 0444); +module_param(tda9840, int, 0444); +module_param(tda9850, int, 0444); +module_param(tda9855, int, 0444); +module_param(tda9873, int, 0444); +module_param(tda9874a, int, 0444); +module_param(tda9875, int, 0444); +module_param(tea6300, int, 0444); +module_param(tea6320, int, 0444); +module_param(tea6420, int, 0444); +module_param(pic16c54, int, 0444); +module_param(ta8874z, int, 0444); + +static struct CHIPDESC chiplist[] = { + { + .name = "tda9840", + .insmodopt = &tda9840, + .addr_lo = I2C_ADDR_TDA9840 >> 1, + .addr_hi = I2C_ADDR_TDA9840 >> 1, + .registers = 5, + .flags = CHIP_NEED_CHECKMODE, + + /* callbacks */ + .checkit = tda9840_checkit, + .getrxsubchans = tda9840_getrxsubchans, + .setaudmode = tda9840_setaudmode, + + .init = { 2, { TDA9840_TEST, TDA9840_TEST_INT1SN + /* ,TDA9840_SW, TDA9840_MONO */} } + }, + { + .name = "tda9873h", + .insmodopt = &tda9873, + .addr_lo = I2C_ADDR_TDA985x_L >> 1, + .addr_hi = I2C_ADDR_TDA985x_H >> 1, + .registers = 3, + .flags = CHIP_HAS_INPUTSEL | CHIP_NEED_CHECKMODE, + + /* callbacks */ + .checkit = tda9873_checkit, + .getrxsubchans = tda9873_getrxsubchans, + .setaudmode = tda9873_setaudmode, + + .init = { 4, { TDA9873_SW, 0xa4, 0x06, 0x03 } }, + .inputreg = TDA9873_SW, + .inputmute = TDA9873_MUTE | TDA9873_AUTOMUTE, + .inputmap = {0xa0, 0xa2, 0xa0, 0xa0}, + .inputmask = TDA9873_INP_MASK|TDA9873_MUTE|TDA9873_AUTOMUTE, + + }, + { + .name = "tda9874h/a", + .insmodopt = &tda9874a, + .addr_lo = I2C_ADDR_TDA9874 >> 1, + .addr_hi = I2C_ADDR_TDA9874 >> 1, + .flags = CHIP_NEED_CHECKMODE, + + /* callbacks */ + .initialize = tda9874a_initialize, + .checkit = tda9874a_checkit, + .getrxsubchans = tda9874a_getrxsubchans, + .setaudmode = tda9874a_setaudmode, + }, + { + .name = "tda9875", + .insmodopt = &tda9875, + .addr_lo = I2C_ADDR_TDA9875 >> 1, + .addr_hi = I2C_ADDR_TDA9875 >> 1, + .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE, + + /* callbacks */ + .initialize = tda9875_initialize, + .checkit = tda9875_checkit, + .volfunc = tda9875_volume, + .bassfunc = tda9875_bass, + .treblefunc = tda9875_treble, + .leftreg = TDA9875_MVL, + .rightreg = TDA9875_MVR, + .bassreg = TDA9875_MBA, + .treblereg = TDA9875_MTR, + .leftinit = 58880, + .rightinit = 58880, + }, + { + .name = "tda9850", + .insmodopt = &tda9850, + .addr_lo = I2C_ADDR_TDA985x_L >> 1, + .addr_hi = I2C_ADDR_TDA985x_H >> 1, + .registers = 11, + + .getrxsubchans = tda985x_getrxsubchans, + .setaudmode = tda985x_setaudmode, + + .init = { 8, { TDA9850_C4, 0x08, 0x08, TDA985x_STEREO, 0x07, 0x10, 0x10, 0x03 } } + }, + { + .name = "tda9855", + .insmodopt = &tda9855, + .addr_lo = I2C_ADDR_TDA985x_L >> 1, + .addr_hi = I2C_ADDR_TDA985x_H >> 1, + .registers = 11, + .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE, + + .leftreg = TDA9855_VL, + .rightreg = TDA9855_VR, + .bassreg = TDA9855_BA, + .treblereg = TDA9855_TR, + + /* callbacks */ + .volfunc = tda9855_volume, + .bassfunc = tda9855_bass, + .treblefunc = tda9855_treble, + .getrxsubchans = tda985x_getrxsubchans, + .setaudmode = tda985x_setaudmode, + + .init = { 12, { 0, 0x6f, 0x6f, 0x0e, 0x07<<1, 0x8<<2, + TDA9855_MUTE | TDA9855_AVL | TDA9855_LOUD | TDA9855_INT, + TDA985x_STEREO | TDA9855_LINEAR | TDA9855_TZCM | TDA9855_VZCM, + 0x07, 0x10, 0x10, 0x03 }} + }, + { + .name = "tea6300", + .insmodopt = &tea6300, + .addr_lo = I2C_ADDR_TEA6300 >> 1, + .addr_hi = I2C_ADDR_TEA6300 >> 1, + .registers = 6, + .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL, + + .leftreg = TEA6300_VR, + .rightreg = TEA6300_VL, + .bassreg = TEA6300_BA, + .treblereg = TEA6300_TR, + + /* callbacks */ + .volfunc = tea6300_shift10, + .bassfunc = tea6300_shift12, + .treblefunc = tea6300_shift12, + + .inputreg = TEA6300_S, + .inputmap = { TEA6300_S_SA, TEA6300_S_SB, TEA6300_S_SC }, + .inputmute = TEA6300_S_GMU, + }, + { + .name = "tea6320", + .insmodopt = &tea6320, + .addr_lo = I2C_ADDR_TEA6300 >> 1, + .addr_hi = I2C_ADDR_TEA6300 >> 1, + .registers = 8, + .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL, + + .leftreg = TEA6320_V, + .rightreg = TEA6320_V, + .bassreg = TEA6320_BA, + .treblereg = TEA6320_TR, + + /* callbacks */ + .initialize = tea6320_initialize, + .volfunc = tea6320_volume, + .bassfunc = tea6320_shift11, + .treblefunc = tea6320_shift11, + + .inputreg = TEA6320_S, + .inputmap = { TEA6320_S_SA, TEA6420_S_SB, TEA6300_S_SC, TEA6320_S_SD }, + .inputmute = TEA6300_S_GMU, + }, + { + .name = "tea6420", + .insmodopt = &tea6420, + .addr_lo = I2C_ADDR_TEA6420 >> 1, + .addr_hi = I2C_ADDR_TEA6420 >> 1, + .registers = 1, + .flags = CHIP_HAS_INPUTSEL, + + .inputreg = -1, + .inputmap = { TEA6420_S_SA, TEA6420_S_SB, TEA6420_S_SC }, + .inputmute = TEA6300_S_GMU, + }, + { + .name = "tda8425", + .insmodopt = &tda8425, + .addr_lo = I2C_ADDR_TDA8425 >> 1, + .addr_hi = I2C_ADDR_TDA8425 >> 1, + .registers = 9, + .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL, + + .leftreg = TDA8425_VL, + .rightreg = TDA8425_VR, + .bassreg = TDA8425_BA, + .treblereg = TDA8425_TR, + + /* callbacks */ + .volfunc = tda8425_shift10, + .bassfunc = tda8425_shift12, + .treblefunc = tda8425_shift12, + .setaudmode = tda8425_setaudmode, + + .inputreg = TDA8425_S1, + .inputmap = { TDA8425_S1_CH1, TDA8425_S1_CH1, TDA8425_S1_CH1 }, + .inputmute = TDA8425_S1_OFF, + + }, + { + .name = "pic16c54 (PV951)", + .insmodopt = &pic16c54, + .addr_lo = I2C_ADDR_PIC16C54 >> 1, + .addr_hi = I2C_ADDR_PIC16C54>> 1, + .registers = 2, + .flags = CHIP_HAS_INPUTSEL, + + .inputreg = PIC16C54_REG_MISC, + .inputmap = {PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_TUNER, + PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_LINE, + PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_LINE, + PIC16C54_MISC_SND_MUTE}, + .inputmute = PIC16C54_MISC_SND_MUTE, + }, + { + .name = "ta8874z", + .checkit = ta8874z_checkit, + .insmodopt = &ta8874z, + .addr_lo = I2C_ADDR_TDA9840 >> 1, + .addr_hi = I2C_ADDR_TDA9840 >> 1, + .registers = 2, + + /* callbacks */ + .getrxsubchans = ta8874z_getrxsubchans, + .setaudmode = ta8874z_setaudmode, + + .init = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}}, + }, + { .name = NULL } /* EOF */ +}; + + +/* ---------------------------------------------------------------------- */ + +static int tvaudio_g_ctrl(struct v4l2_subdev *sd, + struct v4l2_control *ctrl) +{ + struct CHIPSTATE *chip = to_state(sd); + struct CHIPDESC *desc = chip->desc; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (!(desc->flags & CHIP_HAS_INPUTSEL)) + break; + ctrl->value=chip->muted; + return 0; + case V4L2_CID_AUDIO_VOLUME: + if (!(desc->flags & CHIP_HAS_VOLUME)) + break; + ctrl->value = max(chip->left,chip->right); + return 0; + case V4L2_CID_AUDIO_BALANCE: + { + int volume; + if (!(desc->flags & CHIP_HAS_VOLUME)) + break; + volume = max(chip->left,chip->right); + if (volume) + ctrl->value=(32768*min(chip->left,chip->right))/volume; + else + ctrl->value=32768; + return 0; + } + case V4L2_CID_AUDIO_BASS: + if (!(desc->flags & CHIP_HAS_BASSTREBLE)) + break; + ctrl->value = chip->bass; + return 0; + case V4L2_CID_AUDIO_TREBLE: + if (!(desc->flags & CHIP_HAS_BASSTREBLE)) + break; + ctrl->value = chip->treble; + return 0; + } + return -EINVAL; +} + +static int tvaudio_s_ctrl(struct v4l2_subdev *sd, + struct v4l2_control *ctrl) +{ + struct CHIPSTATE *chip = to_state(sd); + struct CHIPDESC *desc = chip->desc; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (!(desc->flags & CHIP_HAS_INPUTSEL)) + break; + + if (ctrl->value < 0 || ctrl->value >= 2) + return -ERANGE; + chip->muted = ctrl->value; + if (chip->muted) + chip_write_masked(chip,desc->inputreg,desc->inputmute,desc->inputmask); + else + chip_write_masked(chip,desc->inputreg, + desc->inputmap[chip->input],desc->inputmask); + return 0; + case V4L2_CID_AUDIO_VOLUME: + { + int volume,balance; + + if (!(desc->flags & CHIP_HAS_VOLUME)) + break; + + volume = max(chip->left,chip->right); + if (volume) + balance=(32768*min(chip->left,chip->right))/volume; + else + balance=32768; + + volume=ctrl->value; + chip->left = (min(65536 - balance,32768) * volume) / 32768; + chip->right = (min(balance,volume *(__u16)32768)) / 32768; + + chip_write(chip,desc->leftreg,desc->volfunc(chip->left)); + chip_write(chip,desc->rightreg,desc->volfunc(chip->right)); + + return 0; + } + case V4L2_CID_AUDIO_BALANCE: + { + int volume, balance; + + if (!(desc->flags & CHIP_HAS_VOLUME)) + break; + + volume = max(chip->left, chip->right); + balance = ctrl->value; + chip->left = (min(65536 - balance, 32768) * volume) / 32768; + chip->right = (min(balance, volume * (__u16)32768)) / 32768; + + chip_write(chip, desc->leftreg, desc->volfunc(chip->left)); + chip_write(chip, desc->rightreg, desc->volfunc(chip->right)); + + return 0; + } + case V4L2_CID_AUDIO_BASS: + if (!(desc->flags & CHIP_HAS_BASSTREBLE)) + break; + chip->bass = ctrl->value; + chip_write(chip,desc->bassreg,desc->bassfunc(chip->bass)); + + return 0; + case V4L2_CID_AUDIO_TREBLE: + if (!(desc->flags & CHIP_HAS_BASSTREBLE)) + break; + chip->treble = ctrl->value; + chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble)); + + return 0; + } + return -EINVAL; +} + + +/* ---------------------------------------------------------------------- */ +/* video4linux interface */ + +static int tvaudio_s_radio(struct v4l2_subdev *sd) +{ + struct CHIPSTATE *chip = to_state(sd); + + chip->radio = 1; + /* del_timer(&chip->wt); */ + return 0; +} + +static int tvaudio_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + struct CHIPSTATE *chip = to_state(sd); + struct CHIPDESC *desc = chip->desc; + + switch (qc->id) { + case V4L2_CID_AUDIO_MUTE: + if (desc->flags & CHIP_HAS_INPUTSEL) + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); + break; + case V4L2_CID_AUDIO_VOLUME: + if (desc->flags & CHIP_HAS_VOLUME) + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880); + break; + case V4L2_CID_AUDIO_BALANCE: + if (desc->flags & CHIP_HAS_VOLUME) + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); + break; + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + if (desc->flags & CHIP_HAS_BASSTREBLE) + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); + break; + default: + break; + } + return -EINVAL; +} + +static int tvaudio_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct CHIPSTATE *chip = to_state(sd); + struct CHIPDESC *desc = chip->desc; + + if (!(desc->flags & CHIP_HAS_INPUTSEL)) + return 0; + if (input >= 4) + return -EINVAL; + /* There are four inputs: tuner, radio, extern and intern. */ + chip->input = input; + if (chip->muted) + return 0; + chip_write_masked(chip, desc->inputreg, + desc->inputmap[chip->input], desc->inputmask); + return 0; +} + +static int tvaudio_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct CHIPSTATE *chip = to_state(sd); + struct CHIPDESC *desc = chip->desc; + + if (!desc->setaudmode) + return 0; + if (chip->radio) + return 0; + + switch (vt->audmode) { + case V4L2_TUNER_MODE_MONO: + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1: + case V4L2_TUNER_MODE_LANG2: + case V4L2_TUNER_MODE_LANG1_LANG2: + break; + default: + return -EINVAL; + } + chip->audmode = vt->audmode; + + if (chip->thread) + wake_up_process(chip->thread); + else + desc->setaudmode(chip, vt->audmode); + + return 0; +} + +static int tvaudio_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct CHIPSTATE *chip = to_state(sd); + struct CHIPDESC *desc = chip->desc; + + if (!desc->getrxsubchans) + return 0; + if (chip->radio) + return 0; + + vt->audmode = chip->audmode; + vt->rxsubchans = desc->getrxsubchans(chip); + vt->capability = V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; + + return 0; +} + +static int tvaudio_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct CHIPSTATE *chip = to_state(sd); + + chip->radio = 0; + return 0; +} + +static int tvaudio_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +{ + struct CHIPSTATE *chip = to_state(sd); + struct CHIPDESC *desc = chip->desc; + + /* For chips that provide getrxsubchans and setaudmode, and doesn't + automatically follows the stereo carrier, a kthread is + created to set the audio standard. In this case, when then + the video channel is changed, tvaudio starts on MONO mode. + After waiting for 2 seconds, the kernel thread is called, + to follow whatever audio standard is pointed by the + audio carrier. + */ + if (chip->thread) { + desc->setaudmode(chip, V4L2_TUNER_MODE_MONO); + chip->prevmode = -1; /* reset previous mode */ + mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000)); + } + return 0; +} + +static int tvaudio_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVAUDIO, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tvaudio_core_ops = { + .g_chip_ident = tvaudio_g_chip_ident, + .queryctrl = tvaudio_queryctrl, + .g_ctrl = tvaudio_g_ctrl, + .s_ctrl = tvaudio_s_ctrl, + .s_std = tvaudio_s_std, +}; + +static const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = { + .s_radio = tvaudio_s_radio, + .s_frequency = tvaudio_s_frequency, + .s_tuner = tvaudio_s_tuner, + .g_tuner = tvaudio_g_tuner, +}; + +static const struct v4l2_subdev_audio_ops tvaudio_audio_ops = { + .s_routing = tvaudio_s_routing, +}; + +static const struct v4l2_subdev_ops tvaudio_ops = { + .core = &tvaudio_core_ops, + .tuner = &tvaudio_tuner_ops, + .audio = &tvaudio_audio_ops, +}; + +/* ----------------------------------------------------------------------- */ + + +/* i2c registration */ + +static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct CHIPSTATE *chip; + struct CHIPDESC *desc; + struct v4l2_subdev *sd; + + if (debug) { + printk(KERN_INFO "tvaudio: TV audio decoder + audio/video mux driver\n"); + printk(KERN_INFO "tvaudio: known chips: "); + for (desc = chiplist; desc->name != NULL; desc++) + printk("%s%s", (desc == chiplist) ? "" : ", ", desc->name); + printk("\n"); + } + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + sd = &chip->sd; + v4l2_i2c_subdev_init(sd, client, &tvaudio_ops); + + /* find description for the chip */ + v4l2_dbg(1, debug, sd, "chip found @ 0x%x\n", client->addr<<1); + for (desc = chiplist; desc->name != NULL; desc++) { + if (0 == *(desc->insmodopt)) + continue; + if (client->addr < desc->addr_lo || + client->addr > desc->addr_hi) + continue; + if (desc->checkit && !desc->checkit(chip)) + continue; + break; + } + if (desc->name == NULL) { + v4l2_dbg(1, debug, sd, "no matching chip description found\n"); + kfree(chip); + return -EIO; + } + v4l2_info(sd, "%s found @ 0x%x (%s)\n", desc->name, client->addr<<1, client->adapter->name); + if (desc->flags) { + v4l2_dbg(1, debug, sd, "matches:%s%s%s.\n", + (desc->flags & CHIP_HAS_VOLUME) ? " volume" : "", + (desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "", + (desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : ""); + } + + /* fill required data structures */ + if (!id) + strlcpy(client->name, desc->name, I2C_NAME_SIZE); + chip->desc = desc; + chip->shadow.count = desc->registers+1; + chip->prevmode = -1; + chip->audmode = V4L2_TUNER_MODE_LANG1; + + /* initialization */ + if (desc->initialize != NULL) + desc->initialize(chip); + else + chip_cmd(chip, "init", &desc->init); + + if (desc->flags & CHIP_HAS_VOLUME) { + if (!desc->volfunc) { + /* This shouldn't be happen. Warn user, but keep working + without volume controls + */ + v4l2_info(sd, "volume callback undefined!\n"); + desc->flags &= ~CHIP_HAS_VOLUME; + } else { + chip->left = desc->leftinit ? desc->leftinit : 65535; + chip->right = desc->rightinit ? desc->rightinit : 65535; + chip_write(chip, desc->leftreg, + desc->volfunc(chip->left)); + chip_write(chip, desc->rightreg, + desc->volfunc(chip->right)); + } + } + if (desc->flags & CHIP_HAS_BASSTREBLE) { + if (!desc->bassfunc || !desc->treblefunc) { + /* This shouldn't be happen. Warn user, but keep working + without bass/treble controls + */ + v4l2_info(sd, "bass/treble callbacks undefined!\n"); + desc->flags &= ~CHIP_HAS_BASSTREBLE; + } else { + chip->treble = desc->trebleinit ? + desc->trebleinit : 32768; + chip->bass = desc->bassinit ? + desc->bassinit : 32768; + chip_write(chip, desc->bassreg, + desc->bassfunc(chip->bass)); + chip_write(chip, desc->treblereg, + desc->treblefunc(chip->treble)); + } + } + + chip->thread = NULL; + init_timer(&chip->wt); + if (desc->flags & CHIP_NEED_CHECKMODE) { + if (!desc->getrxsubchans || !desc->setaudmode) { + /* This shouldn't be happen. Warn user, but keep working + without kthread + */ + v4l2_info(sd, "set/get mode callbacks undefined!\n"); + return 0; + } + /* start async thread */ + chip->wt.function = chip_thread_wake; + chip->wt.data = (unsigned long)chip; + chip->thread = kthread_run(chip_thread, chip, client->name); + if (IS_ERR(chip->thread)) { + v4l2_warn(sd, "failed to create kthread\n"); + chip->thread = NULL; + } + } + return 0; +} + +static int tvaudio_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct CHIPSTATE *chip = to_state(sd); + + del_timer_sync(&chip->wt); + if (chip->thread) { + /* shutdown async thread */ + kthread_stop(chip->thread); + chip->thread = NULL; + } + + v4l2_device_unregister_subdev(sd); + kfree(chip); + return 0; +} + +/* This driver supports many devices and the idea is to let the driver + detect which device is present. So rather than listing all supported + devices here, we pretend to support a single, fake device type. */ +static const struct i2c_device_id tvaudio_id[] = { + { "tvaudio", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tvaudio_id); + +static struct i2c_driver tvaudio_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tvaudio", + }, + .probe = tvaudio_probe, + .remove = tvaudio_remove, + .id_table = tvaudio_id, +}; + +module_i2c_driver(tvaudio_driver); diff --git a/drivers/media/i2c/tveeprom.c b/drivers/media/i2c/tveeprom.c new file mode 100644 index 00000000000..3b6cf034976 --- /dev/null +++ b/drivers/media/i2c/tveeprom.c @@ -0,0 +1,792 @@ +/* + * tveeprom - eeprom decoder for tvcard configuration eeproms + * + * Data and decoding routines shamelessly borrowed from bttv-cards.c + * eeprom access routine shamelessly borrowed from bttv-if.c + * which are: + + Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) + & Marcus Metzler (mocm@thp.uni-koeln.de) + (c) 1999-2001 Gerd Knorr + + * Adjustments to fit a more general model and all bugs: + + Copyright (C) 2003 John Klar + + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +MODULE_DESCRIPTION("i2c Hauppauge eeprom decoder driver"); +MODULE_AUTHOR("John Klar"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +#define STRM(array, i) \ + (i < sizeof(array) / sizeof(char *) ? array[i] : "unknown") + +#define tveeprom_info(fmt, arg...) \ + v4l_printk(KERN_INFO, "tveeprom", c->adapter, c->addr, fmt , ## arg) +#define tveeprom_warn(fmt, arg...) \ + v4l_printk(KERN_WARNING, "tveeprom", c->adapter, c->addr, fmt , ## arg) +#define tveeprom_dbg(fmt, arg...) do { \ + if (debug) \ + v4l_printk(KERN_DEBUG, "tveeprom", \ + c->adapter, c->addr, fmt , ## arg); \ + } while (0) + +/* + * The Hauppauge eeprom uses an 8bit field to determine which + * tuner formats the tuner supports. + */ +static struct HAUPPAUGE_TUNER_FMT +{ + int id; + char *name; +} +hauppauge_tuner_fmt[] = +{ + { V4L2_STD_UNKNOWN, " UNKNOWN" }, + { V4L2_STD_UNKNOWN, " FM" }, + { V4L2_STD_B|V4L2_STD_GH, " PAL(B/G)" }, + { V4L2_STD_MN, " NTSC(M)" }, + { V4L2_STD_PAL_I, " PAL(I)" }, + { V4L2_STD_SECAM_L|V4L2_STD_SECAM_LC, " SECAM(L/L')" }, + { V4L2_STD_DK, " PAL(D/D1/K)" }, + { V4L2_STD_ATSC, " ATSC/DVB Digital" }, +}; + +/* This is the full list of possible tuners. Many thanks to Hauppauge for + supplying this information. Note that many tuners where only used for + testing and never made it to the outside world. So you will only see + a subset in actual produced cards. */ +static struct HAUPPAUGE_TUNER +{ + int id; + char *name; +} +hauppauge_tuner[] = +{ + /* 0-9 */ + { TUNER_ABSENT, "None" }, + { TUNER_ABSENT, "External" }, + { TUNER_ABSENT, "Unspecified" }, + { TUNER_PHILIPS_PAL, "Philips FI1216" }, + { TUNER_PHILIPS_SECAM, "Philips FI1216MF" }, + { TUNER_PHILIPS_NTSC, "Philips FI1236" }, + { TUNER_PHILIPS_PAL_I, "Philips FI1246" }, + { TUNER_PHILIPS_PAL_DK, "Philips FI1256" }, + { TUNER_PHILIPS_PAL, "Philips FI1216 MK2" }, + { TUNER_PHILIPS_SECAM, "Philips FI1216MF MK2" }, + /* 10-19 */ + { TUNER_PHILIPS_NTSC, "Philips FI1236 MK2" }, + { TUNER_PHILIPS_PAL_I, "Philips FI1246 MK2" }, + { TUNER_PHILIPS_PAL_DK, "Philips FI1256 MK2" }, + { TUNER_TEMIC_NTSC, "Temic 4032FY5" }, + { TUNER_TEMIC_PAL, "Temic 4002FH5" }, + { TUNER_TEMIC_PAL_I, "Temic 4062FY5" }, + { TUNER_PHILIPS_PAL, "Philips FR1216 MK2" }, + { TUNER_PHILIPS_SECAM, "Philips FR1216MF MK2" }, + { TUNER_PHILIPS_NTSC, "Philips FR1236 MK2" }, + { TUNER_PHILIPS_PAL_I, "Philips FR1246 MK2" }, + /* 20-29 */ + { TUNER_PHILIPS_PAL_DK, "Philips FR1256 MK2" }, + { TUNER_PHILIPS_PAL, "Philips FM1216" }, + { TUNER_PHILIPS_SECAM, "Philips FM1216MF" }, + { TUNER_PHILIPS_NTSC, "Philips FM1236" }, + { TUNER_PHILIPS_PAL_I, "Philips FM1246" }, + { TUNER_PHILIPS_PAL_DK, "Philips FM1256" }, + { TUNER_TEMIC_4036FY5_NTSC, "Temic 4036FY5" }, + { TUNER_ABSENT, "Samsung TCPN9082D" }, + { TUNER_ABSENT, "Samsung TCPM9092P" }, + { TUNER_TEMIC_4006FH5_PAL, "Temic 4006FH5" }, + /* 30-39 */ + { TUNER_ABSENT, "Samsung TCPN9085D" }, + { TUNER_ABSENT, "Samsung TCPB9085P" }, + { TUNER_ABSENT, "Samsung TCPL9091P" }, + { TUNER_TEMIC_4039FR5_NTSC, "Temic 4039FR5" }, + { TUNER_PHILIPS_FQ1216ME, "Philips FQ1216 ME" }, + { TUNER_TEMIC_4066FY5_PAL_I, "Temic 4066FY5" }, + { TUNER_PHILIPS_NTSC, "Philips TD1536" }, + { TUNER_PHILIPS_NTSC, "Philips TD1536D" }, + { TUNER_PHILIPS_NTSC, "Philips FMR1236" }, /* mono radio */ + { TUNER_ABSENT, "Philips FI1256MP" }, + /* 40-49 */ + { TUNER_ABSENT, "Samsung TCPQ9091P" }, + { TUNER_TEMIC_4006FN5_MULTI_PAL, "Temic 4006FN5" }, + { TUNER_TEMIC_4009FR5_PAL, "Temic 4009FR5" }, + { TUNER_TEMIC_4046FM5, "Temic 4046FM5" }, + { TUNER_TEMIC_4009FN5_MULTI_PAL_FM, "Temic 4009FN5" }, + { TUNER_ABSENT, "Philips TD1536D FH 44"}, + { TUNER_LG_NTSC_FM, "LG TP18NSR01F"}, + { TUNER_LG_PAL_FM, "LG TP18PSB01D"}, + { TUNER_LG_PAL, "LG TP18PSB11D"}, + { TUNER_LG_PAL_I_FM, "LG TAPC-I001D"}, + /* 50-59 */ + { TUNER_LG_PAL_I, "LG TAPC-I701D"}, + { TUNER_ABSENT, "Temic 4042FI5"}, + { TUNER_MICROTUNE_4049FM5, "Microtune 4049 FM5"}, + { TUNER_ABSENT, "LG TPI8NSR11F"}, + { TUNER_ABSENT, "Microtune 4049 FM5 Alt I2C"}, + { TUNER_PHILIPS_FM1216ME_MK3, "Philips FQ1216ME MK3"}, + { TUNER_ABSENT, "Philips FI1236 MK3"}, + { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216 ME MK3"}, + { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK3"}, + { TUNER_ABSENT, "Philips FM1216MP MK3"}, + /* 60-69 */ + { TUNER_PHILIPS_FM1216ME_MK3, "LG S001D MK3"}, + { TUNER_ABSENT, "LG M001D MK3"}, + { TUNER_PHILIPS_FM1216ME_MK3, "LG S701D MK3"}, + { TUNER_ABSENT, "LG M701D MK3"}, + { TUNER_ABSENT, "Temic 4146FM5"}, + { TUNER_ABSENT, "Temic 4136FY5"}, + { TUNER_ABSENT, "Temic 4106FH5"}, + { TUNER_ABSENT, "Philips FQ1216LMP MK3"}, + { TUNER_LG_NTSC_TAPE, "LG TAPE H001F MK3"}, + { TUNER_LG_NTSC_TAPE, "LG TAPE H701F MK3"}, + /* 70-79 */ + { TUNER_ABSENT, "LG TALN H200T"}, + { TUNER_ABSENT, "LG TALN H250T"}, + { TUNER_ABSENT, "LG TALN M200T"}, + { TUNER_ABSENT, "LG TALN Z200T"}, + { TUNER_ABSENT, "LG TALN S200T"}, + { TUNER_ABSENT, "Thompson DTT7595"}, + { TUNER_ABSENT, "Thompson DTT7592"}, + { TUNER_ABSENT, "Silicon TDA8275C1 8290"}, + { TUNER_ABSENT, "Silicon TDA8275C1 8290 FM"}, + { TUNER_ABSENT, "Thompson DTT757"}, + /* 80-89 */ + { TUNER_PHILIPS_FQ1216LME_MK3, "Philips FQ1216LME MK3"}, + { TUNER_LG_PAL_NEW_TAPC, "LG TAPC G701D"}, + { TUNER_LG_NTSC_NEW_TAPC, "LG TAPC H791F"}, + { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MB 3"}, + { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MI 3"}, + { TUNER_TCL_2002N, "TCL 2002N 6A"}, + { TUNER_PHILIPS_FM1236_MK3, "Philips FQ1236 MK3"}, + { TUNER_SAMSUNG_TCPN_2121P30A, "Samsung TCPN 2121P30A"}, + { TUNER_ABSENT, "Samsung TCPE 4121P30A"}, + { TUNER_PHILIPS_FM1216ME_MK3, "TCL MFPE05 2"}, + /* 90-99 */ + { TUNER_ABSENT, "LG TALN H202T"}, + { TUNER_PHILIPS_FQ1216AME_MK4, "Philips FQ1216AME MK4"}, + { TUNER_PHILIPS_FQ1236A_MK4, "Philips FQ1236A MK4"}, + { TUNER_ABSENT, "Philips FQ1286A MK4"}, + { TUNER_ABSENT, "Philips FQ1216ME MK5"}, + { TUNER_ABSENT, "Philips FQ1236 MK5"}, + { TUNER_SAMSUNG_TCPG_6121P30A, "Samsung TCPG 6121P30A"}, + { TUNER_TCL_2002MB, "TCL 2002MB_3H"}, + { TUNER_ABSENT, "TCL 2002MI_3H"}, + { TUNER_TCL_2002N, "TCL 2002N 5H"}, + /* 100-109 */ + { TUNER_PHILIPS_FMD1216ME_MK3, "Philips FMD1216ME"}, + { TUNER_TEA5767, "Philips TEA5768HL FM Radio"}, + { TUNER_ABSENT, "Panasonic ENV57H12D5"}, + { TUNER_PHILIPS_FM1236_MK3, "TCL MFNM05-4"}, + { TUNER_PHILIPS_FM1236_MK3, "TCL MNM05-4"}, + { TUNER_PHILIPS_FM1216ME_MK3, "TCL MPE05-2"}, + { TUNER_ABSENT, "TCL MQNM05-4"}, + { TUNER_ABSENT, "LG TAPC-W701D"}, + { TUNER_ABSENT, "TCL 9886P-WM"}, + { TUNER_ABSENT, "TCL 1676NM-WM"}, + /* 110-119 */ + { TUNER_ABSENT, "Thompson DTT75105"}, + { TUNER_ABSENT, "Conexant_CX24109"}, + { TUNER_TCL_2002N, "TCL M2523_5N_E"}, + { TUNER_TCL_2002MB, "TCL M2523_3DB_E"}, + { TUNER_ABSENT, "Philips 8275A"}, + { TUNER_ABSENT, "Microtune MT2060"}, + { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK5"}, + { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216ME MK5"}, + { TUNER_ABSENT, "TCL M2523_3DI_E"}, + { TUNER_ABSENT, "Samsung THPD5222FG30A"}, + /* 120-129 */ + { TUNER_XC2028, "Xceive XC3028"}, + { TUNER_PHILIPS_FQ1216LME_MK3, "Philips FQ1216LME MK5"}, + { TUNER_ABSENT, "Philips FQD1216LME"}, + { TUNER_ABSENT, "Conexant CX24118A"}, + { TUNER_ABSENT, "TCL DMF11WIP"}, + { TUNER_ABSENT, "TCL MFNM05_4H_E"}, + { TUNER_ABSENT, "TCL MNM05_4H_E"}, + { TUNER_ABSENT, "TCL MPE05_2H_E"}, + { TUNER_ABSENT, "TCL MQNM05_4_U"}, + { TUNER_ABSENT, "TCL M2523_5NH_E"}, + /* 130-139 */ + { TUNER_ABSENT, "TCL M2523_3DBH_E"}, + { TUNER_ABSENT, "TCL M2523_3DIH_E"}, + { TUNER_ABSENT, "TCL MFPE05_2_U"}, + { TUNER_PHILIPS_FMD1216MEX_MK3, "Philips FMD1216MEX"}, + { TUNER_ABSENT, "Philips FRH2036B"}, + { TUNER_ABSENT, "Panasonic ENGF75_01GF"}, + { TUNER_ABSENT, "MaxLinear MXL5005"}, + { TUNER_ABSENT, "MaxLinear MXL5003"}, + { TUNER_ABSENT, "Xceive XC2028"}, + { TUNER_ABSENT, "Microtune MT2131"}, + /* 140-149 */ + { TUNER_ABSENT, "Philips 8275A_8295"}, + { TUNER_ABSENT, "TCL MF02GIP_5N_E"}, + { TUNER_ABSENT, "TCL MF02GIP_3DB_E"}, + { TUNER_ABSENT, "TCL MF02GIP_3DI_E"}, + { TUNER_ABSENT, "Microtune MT2266"}, + { TUNER_ABSENT, "TCL MF10WPP_4N_E"}, + { TUNER_ABSENT, "LG TAPQ_H702F"}, + { TUNER_ABSENT, "TCL M09WPP_4N_E"}, + { TUNER_ABSENT, "MaxLinear MXL5005_v2"}, + { TUNER_PHILIPS_TDA8290, "Philips 18271_8295"}, + /* 150-159 */ + { TUNER_XC5000, "Xceive XC5000"}, + { TUNER_ABSENT, "Xceive XC3028L"}, + { TUNER_ABSENT, "NXP 18271C2_716x"}, + { TUNER_ABSENT, "Xceive XC4000"}, + { TUNER_ABSENT, "Dibcom 7070"}, + { TUNER_PHILIPS_TDA8290, "NXP 18271C2"}, + { TUNER_ABSENT, "Siano SMS1010"}, + { TUNER_ABSENT, "Siano SMS1150"}, + { TUNER_ABSENT, "MaxLinear 5007"}, + { TUNER_ABSENT, "TCL M09WPP_2P_E"}, + /* 160-169 */ + { TUNER_ABSENT, "Siano SMS1180"}, + { TUNER_ABSENT, "Maxim_MAX2165"}, + { TUNER_ABSENT, "Siano SMS1140"}, + { TUNER_ABSENT, "Siano SMS1150 B1"}, + { TUNER_ABSENT, "MaxLinear 111"}, + { TUNER_ABSENT, "Dibcom 7770"}, + { TUNER_ABSENT, "Siano SMS1180VNS"}, + { TUNER_ABSENT, "Siano SMS1184"}, + { TUNER_PHILIPS_FQ1236_MK5, "TCL M30WTP-4N-E"}, + { TUNER_ABSENT, "TCL_M11WPP_2PN_E"}, + /* 170-179 */ + { TUNER_ABSENT, "MaxLinear 301"}, + { TUNER_ABSENT, "Mirics MSi001"}, + { TUNER_ABSENT, "MaxLinear MxL241SF"}, + { TUNER_XC5000C, "Xceive XC5000C"}, + { TUNER_ABSENT, "Montage M68TS2020"}, + { TUNER_ABSENT, "Siano SMS1530"}, + { TUNER_ABSENT, "Dibcom 7090"}, + { TUNER_ABSENT, "Xceive XC5200C"}, + { TUNER_ABSENT, "NXP 18273"}, + { TUNER_ABSENT, "Montage M88TS2022"}, + /* 180-189 */ + { TUNER_ABSENT, "NXP 18272M"}, + { TUNER_ABSENT, "NXP 18272S"}, +}; + +/* Use V4L2_IDENT_AMBIGUOUS for those audio 'chips' that are + * internal to a video chip, i.e. not a separate audio chip. */ +static struct HAUPPAUGE_AUDIOIC +{ + u32 id; + char *name; +} +audioIC[] = +{ + /* 0-4 */ + { V4L2_IDENT_NONE, "None" }, + { V4L2_IDENT_UNKNOWN, "TEA6300" }, + { V4L2_IDENT_UNKNOWN, "TEA6320" }, + { V4L2_IDENT_UNKNOWN, "TDA9850" }, + { V4L2_IDENT_MSPX4XX, "MSP3400C" }, + /* 5-9 */ + { V4L2_IDENT_MSPX4XX, "MSP3410D" }, + { V4L2_IDENT_MSPX4XX, "MSP3415" }, + { V4L2_IDENT_MSPX4XX, "MSP3430" }, + { V4L2_IDENT_MSPX4XX, "MSP3438" }, + { V4L2_IDENT_UNKNOWN, "CS5331" }, + /* 10-14 */ + { V4L2_IDENT_MSPX4XX, "MSP3435" }, + { V4L2_IDENT_MSPX4XX, "MSP3440" }, + { V4L2_IDENT_MSPX4XX, "MSP3445" }, + { V4L2_IDENT_MSPX4XX, "MSP3411" }, + { V4L2_IDENT_MSPX4XX, "MSP3416" }, + /* 15-19 */ + { V4L2_IDENT_MSPX4XX, "MSP3425" }, + { V4L2_IDENT_MSPX4XX, "MSP3451" }, + { V4L2_IDENT_MSPX4XX, "MSP3418" }, + { V4L2_IDENT_UNKNOWN, "Type 0x12" }, + { V4L2_IDENT_UNKNOWN, "OKI7716" }, + /* 20-24 */ + { V4L2_IDENT_MSPX4XX, "MSP4410" }, + { V4L2_IDENT_MSPX4XX, "MSP4420" }, + { V4L2_IDENT_MSPX4XX, "MSP4440" }, + { V4L2_IDENT_MSPX4XX, "MSP4450" }, + { V4L2_IDENT_MSPX4XX, "MSP4408" }, + /* 25-29 */ + { V4L2_IDENT_MSPX4XX, "MSP4418" }, + { V4L2_IDENT_MSPX4XX, "MSP4428" }, + { V4L2_IDENT_MSPX4XX, "MSP4448" }, + { V4L2_IDENT_MSPX4XX, "MSP4458" }, + { V4L2_IDENT_MSPX4XX, "Type 0x1d" }, + /* 30-34 */ + { V4L2_IDENT_AMBIGUOUS, "CX880" }, + { V4L2_IDENT_AMBIGUOUS, "CX881" }, + { V4L2_IDENT_AMBIGUOUS, "CX883" }, + { V4L2_IDENT_AMBIGUOUS, "CX882" }, + { V4L2_IDENT_AMBIGUOUS, "CX25840" }, + /* 35-39 */ + { V4L2_IDENT_AMBIGUOUS, "CX25841" }, + { V4L2_IDENT_AMBIGUOUS, "CX25842" }, + { V4L2_IDENT_AMBIGUOUS, "CX25843" }, + { V4L2_IDENT_AMBIGUOUS, "CX23418" }, + { V4L2_IDENT_AMBIGUOUS, "CX23885" }, + /* 40-44 */ + { V4L2_IDENT_AMBIGUOUS, "CX23888" }, + { V4L2_IDENT_AMBIGUOUS, "SAA7131" }, + { V4L2_IDENT_AMBIGUOUS, "CX23887" }, + { V4L2_IDENT_AMBIGUOUS, "SAA7164" }, + { V4L2_IDENT_AMBIGUOUS, "AU8522" }, +}; + +/* This list is supplied by Hauppauge. Thanks! */ +static const char *decoderIC[] = { + /* 0-4 */ + "None", "BT815", "BT817", "BT819", "BT815A", + /* 5-9 */ + "BT817A", "BT819A", "BT827", "BT829", "BT848", + /* 10-14 */ + "BT848A", "BT849A", "BT829A", "BT827A", "BT878", + /* 15-19 */ + "BT879", "BT880", "VPX3226E", "SAA7114", "SAA7115", + /* 20-24 */ + "CX880", "CX881", "CX883", "SAA7111", "SAA7113", + /* 25-29 */ + "CX882", "TVP5150A", "CX25840", "CX25841", "CX25842", + /* 30-34 */ + "CX25843", "CX23418", "NEC61153", "CX23885", "CX23888", + /* 35-39 */ + "SAA7131", "CX25837", "CX23887", "CX23885A", "CX23887A", + /* 40-42 */ + "SAA7164", "CX23885B", "AU8522" +}; + +static int hasRadioTuner(int tunerType) +{ + switch (tunerType) { + case 18: /* PNPEnv_TUNER_FR1236_MK2 */ + case 23: /* PNPEnv_TUNER_FM1236 */ + case 38: /* PNPEnv_TUNER_FMR1236 */ + case 16: /* PNPEnv_TUNER_FR1216_MK2 */ + case 19: /* PNPEnv_TUNER_FR1246_MK2 */ + case 21: /* PNPEnv_TUNER_FM1216 */ + case 24: /* PNPEnv_TUNER_FM1246 */ + case 17: /* PNPEnv_TUNER_FR1216MF_MK2 */ + case 22: /* PNPEnv_TUNER_FM1216MF */ + case 20: /* PNPEnv_TUNER_FR1256_MK2 */ + case 25: /* PNPEnv_TUNER_FM1256 */ + case 33: /* PNPEnv_TUNER_4039FR5 */ + case 42: /* PNPEnv_TUNER_4009FR5 */ + case 52: /* PNPEnv_TUNER_4049FM5 */ + case 54: /* PNPEnv_TUNER_4049FM5_AltI2C */ + case 44: /* PNPEnv_TUNER_4009FN5 */ + case 31: /* PNPEnv_TUNER_TCPB9085P */ + case 30: /* PNPEnv_TUNER_TCPN9085D */ + case 46: /* PNPEnv_TUNER_TP18NSR01F */ + case 47: /* PNPEnv_TUNER_TP18PSB01D */ + case 49: /* PNPEnv_TUNER_TAPC_I001D */ + case 60: /* PNPEnv_TUNER_TAPE_S001D_MK3 */ + case 57: /* PNPEnv_TUNER_FM1216ME_MK3 */ + case 59: /* PNPEnv_TUNER_FM1216MP_MK3 */ + case 58: /* PNPEnv_TUNER_FM1236_MK3 */ + case 68: /* PNPEnv_TUNER_TAPE_H001F_MK3 */ + case 61: /* PNPEnv_TUNER_TAPE_M001D_MK3 */ + case 78: /* PNPEnv_TUNER_TDA8275C1_8290_FM */ + case 89: /* PNPEnv_TUNER_TCL_MFPE05_2 */ + case 92: /* PNPEnv_TUNER_PHILIPS_FQ1236A_MK4 */ + case 105: + return 1; + } + return 0; +} + +void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, + unsigned char *eeprom_data) +{ + /* ---------------------------------------------- + ** The hauppauge eeprom format is tagged + ** + ** if packet[0] == 0x84, then packet[0..1] == length + ** else length = packet[0] & 3f; + ** if packet[0] & f8 == f8, then EOD and packet[1] == checksum + ** + ** In our (ivtv) case we're interested in the following: + ** tuner type: tag [00].05 or [0a].01 (index into hauppauge_tuner) + ** tuner fmts: tag [00].04 or [0a].00 (bitmask index into + ** hauppauge_tuner_fmt) + ** radio: tag [00].{last} or [0e].00 (bitmask. bit2=FM) + ** audio proc: tag [02].01 or [05].00 (mask with 0x7f) + ** decoder proc: tag [09].01) + + ** Fun info: + ** model: tag [00].07-08 or [06].00-01 + ** revision: tag [00].09-0b or [06].04-06 + ** serial#: tag [01].05-07 or [04].04-06 + + ** # of inputs/outputs ??? + */ + + int i, j, len, done, beenhere, tag, start; + + int tuner1 = 0, t_format1 = 0, audioic = -1; + char *t_name1 = NULL; + const char *t_fmt_name1[8] = { " none", "", "", "", "", "", "", "" }; + + int tuner2 = 0, t_format2 = 0; + char *t_name2 = NULL; + const char *t_fmt_name2[8] = { " none", "", "", "", "", "", "", "" }; + + memset(tvee, 0, sizeof(*tvee)); + tvee->tuner_type = TUNER_ABSENT; + tvee->tuner2_type = TUNER_ABSENT; + + done = len = beenhere = 0; + + /* Different eeprom start offsets for em28xx, cx2388x and cx23418 */ + if (eeprom_data[0] == 0x1a && + eeprom_data[1] == 0xeb && + eeprom_data[2] == 0x67 && + eeprom_data[3] == 0x95) + start = 0xa0; /* Generic em28xx offset */ + else if ((eeprom_data[0] & 0xe1) == 0x01 && + eeprom_data[1] == 0x00 && + eeprom_data[2] == 0x00 && + eeprom_data[8] == 0x84) + start = 8; /* Generic cx2388x offset */ + else if (eeprom_data[1] == 0x70 && + eeprom_data[2] == 0x00 && + eeprom_data[4] == 0x74 && + eeprom_data[8] == 0x84) + start = 8; /* Generic cx23418 offset (models 74xxx) */ + else + start = 0; + + for (i = start; !done && i < 256; i += len) { + if (eeprom_data[i] == 0x84) { + len = eeprom_data[i + 1] + (eeprom_data[i + 2] << 8); + i += 3; + } else if ((eeprom_data[i] & 0xf0) == 0x70) { + if (eeprom_data[i] & 0x08) { + /* verify checksum! */ + done = 1; + break; + } + len = eeprom_data[i] & 0x07; + ++i; + } else { + tveeprom_warn("Encountered bad packet header [%02x]. " + "Corrupt or not a Hauppauge eeprom.\n", + eeprom_data[i]); + return; + } + + if (debug) { + tveeprom_info("Tag [%02x] + %d bytes:", + eeprom_data[i], len - 1); + for (j = 1; j < len; j++) + printk(KERN_CONT " %02x", eeprom_data[i + j]); + printk(KERN_CONT "\n"); + } + + /* process by tag */ + tag = eeprom_data[i]; + switch (tag) { + case 0x00: + /* tag: 'Comprehensive' */ + tuner1 = eeprom_data[i+6]; + t_format1 = eeprom_data[i+5]; + tvee->has_radio = eeprom_data[i+len-1]; + /* old style tag, don't know how to detect + IR presence, mark as unknown. */ + tvee->has_ir = 0; + tvee->model = + eeprom_data[i+8] + + (eeprom_data[i+9] << 8); + tvee->revision = eeprom_data[i+10] + + (eeprom_data[i+11] << 8) + + (eeprom_data[i+12] << 16); + break; + + case 0x01: + /* tag: 'SerialID' */ + tvee->serial_number = + eeprom_data[i+6] + + (eeprom_data[i+7] << 8) + + (eeprom_data[i+8] << 16); + break; + + case 0x02: + /* tag 'AudioInfo' + Note mask with 0x7F, high bit used on some older models + to indicate 4052 mux was removed in favor of using MSP + inputs directly. */ + audioic = eeprom_data[i+2] & 0x7f; + if (audioic < ARRAY_SIZE(audioIC)) + tvee->audio_processor = audioIC[audioic].id; + else + tvee->audio_processor = V4L2_IDENT_UNKNOWN; + break; + + /* case 0x03: tag 'EEInfo' */ + + case 0x04: + /* tag 'SerialID2' */ + tvee->serial_number = + eeprom_data[i+5] + + (eeprom_data[i+6] << 8) + + (eeprom_data[i+7] << 16); + + if ((eeprom_data[i + 8] & 0xf0) && + (tvee->serial_number < 0xffffff)) { + tvee->MAC_address[0] = 0x00; + tvee->MAC_address[1] = 0x0D; + tvee->MAC_address[2] = 0xFE; + tvee->MAC_address[3] = eeprom_data[i + 7]; + tvee->MAC_address[4] = eeprom_data[i + 6]; + tvee->MAC_address[5] = eeprom_data[i + 5]; + tvee->has_MAC_address = 1; + } + break; + + case 0x05: + /* tag 'Audio2' + Note mask with 0x7F, high bit used on some older models + to indicate 4052 mux was removed in favor of using MSP + inputs directly. */ + audioic = eeprom_data[i+1] & 0x7f; + if (audioic < ARRAY_SIZE(audioIC)) + tvee->audio_processor = audioIC[audioic].id; + else + tvee->audio_processor = V4L2_IDENT_UNKNOWN; + + break; + + case 0x06: + /* tag 'ModelRev' */ + tvee->model = + eeprom_data[i + 1] + + (eeprom_data[i + 2] << 8) + + (eeprom_data[i + 3] << 16) + + (eeprom_data[i + 4] << 24); + tvee->revision = + eeprom_data[i + 5] + + (eeprom_data[i + 6] << 8) + + (eeprom_data[i + 7] << 16); + break; + + case 0x07: + /* tag 'Details': according to Hauppauge not interesting + on any PCI-era or later boards. */ + break; + + /* there is no tag 0x08 defined */ + + case 0x09: + /* tag 'Video' */ + tvee->decoder_processor = eeprom_data[i + 1]; + break; + + case 0x0a: + /* tag 'Tuner' */ + if (beenhere == 0) { + tuner1 = eeprom_data[i + 2]; + t_format1 = eeprom_data[i + 1]; + beenhere = 1; + } else { + /* a second (radio) tuner may be present */ + tuner2 = eeprom_data[i + 2]; + t_format2 = eeprom_data[i + 1]; + /* not a TV tuner? */ + if (t_format2 == 0) + tvee->has_radio = 1; /* must be radio */ + } + break; + + case 0x0b: + /* tag 'Inputs': according to Hauppauge this is specific + to each driver family, so no good assumptions can be + made. */ + break; + + /* case 0x0c: tag 'Balun' */ + /* case 0x0d: tag 'Teletext' */ + + case 0x0e: + /* tag: 'Radio' */ + tvee->has_radio = eeprom_data[i+1]; + break; + + case 0x0f: + /* tag 'IRInfo' */ + tvee->has_ir = 1 | (eeprom_data[i+1] << 1); + break; + + /* case 0x10: tag 'VBIInfo' */ + /* case 0x11: tag 'QCInfo' */ + /* case 0x12: tag 'InfoBits' */ + + default: + tveeprom_dbg("Not sure what to do with tag [%02x]\n", + tag); + /* dump the rest of the packet? */ + } + } + + if (!done) { + tveeprom_warn("Ran out of data!\n"); + return; + } + + if (tvee->revision != 0) { + tvee->rev_str[0] = 32 + ((tvee->revision >> 18) & 0x3f); + tvee->rev_str[1] = 32 + ((tvee->revision >> 12) & 0x3f); + tvee->rev_str[2] = 32 + ((tvee->revision >> 6) & 0x3f); + tvee->rev_str[3] = 32 + (tvee->revision & 0x3f); + tvee->rev_str[4] = 0; + } + + if (hasRadioTuner(tuner1) && !tvee->has_radio) { + tveeprom_info("The eeprom says no radio is present, but the tuner type\n"); + tveeprom_info("indicates otherwise. I will assume that radio is present.\n"); + tvee->has_radio = 1; + } + + if (tuner1 < ARRAY_SIZE(hauppauge_tuner)) { + tvee->tuner_type = hauppauge_tuner[tuner1].id; + t_name1 = hauppauge_tuner[tuner1].name; + } else { + t_name1 = "unknown"; + } + + if (tuner2 < ARRAY_SIZE(hauppauge_tuner)) { + tvee->tuner2_type = hauppauge_tuner[tuner2].id; + t_name2 = hauppauge_tuner[tuner2].name; + } else { + t_name2 = "unknown"; + } + + tvee->tuner_hauppauge_model = tuner1; + tvee->tuner2_hauppauge_model = tuner2; + tvee->tuner_formats = 0; + tvee->tuner2_formats = 0; + for (i = j = 0; i < 8; i++) { + if (t_format1 & (1 << i)) { + tvee->tuner_formats |= hauppauge_tuner_fmt[i].id; + t_fmt_name1[j++] = hauppauge_tuner_fmt[i].name; + } + } + for (i = j = 0; i < 8; i++) { + if (t_format2 & (1 << i)) { + tvee->tuner2_formats |= hauppauge_tuner_fmt[i].id; + t_fmt_name2[j++] = hauppauge_tuner_fmt[i].name; + } + } + + tveeprom_info("Hauppauge model %d, rev %s, serial# %d\n", + tvee->model, tvee->rev_str, tvee->serial_number); + if (tvee->has_MAC_address == 1) + tveeprom_info("MAC address is %pM\n", tvee->MAC_address); + tveeprom_info("tuner model is %s (idx %d, type %d)\n", + t_name1, tuner1, tvee->tuner_type); + tveeprom_info("TV standards%s%s%s%s%s%s%s%s (eeprom 0x%02x)\n", + t_fmt_name1[0], t_fmt_name1[1], t_fmt_name1[2], + t_fmt_name1[3], t_fmt_name1[4], t_fmt_name1[5], + t_fmt_name1[6], t_fmt_name1[7], t_format1); + if (tuner2) + tveeprom_info("second tuner model is %s (idx %d, type %d)\n", + t_name2, tuner2, tvee->tuner2_type); + if (t_format2) + tveeprom_info("TV standards%s%s%s%s%s%s%s%s (eeprom 0x%02x)\n", + t_fmt_name2[0], t_fmt_name2[1], t_fmt_name2[2], + t_fmt_name2[3], t_fmt_name2[4], t_fmt_name2[5], + t_fmt_name2[6], t_fmt_name2[7], t_format2); + if (audioic < 0) { + tveeprom_info("audio processor is unknown (no idx)\n"); + tvee->audio_processor = V4L2_IDENT_UNKNOWN; + } else { + if (audioic < ARRAY_SIZE(audioIC)) + tveeprom_info("audio processor is %s (idx %d)\n", + audioIC[audioic].name, audioic); + else + tveeprom_info("audio processor is unknown (idx %d)\n", + audioic); + } + if (tvee->decoder_processor) + tveeprom_info("decoder processor is %s (idx %d)\n", + STRM(decoderIC, tvee->decoder_processor), + tvee->decoder_processor); + if (tvee->has_ir) + tveeprom_info("has %sradio, has %sIR receiver, has %sIR transmitter\n", + tvee->has_radio ? "" : "no ", + (tvee->has_ir & 2) ? "" : "no ", + (tvee->has_ir & 4) ? "" : "no "); + else + tveeprom_info("has %sradio\n", + tvee->has_radio ? "" : "no "); +} +EXPORT_SYMBOL(tveeprom_hauppauge_analog); + +/* ----------------------------------------------------------------------- */ +/* generic helper functions */ + +int tveeprom_read(struct i2c_client *c, unsigned char *eedata, int len) +{ + unsigned char buf; + int err; + + buf = 0; + err = i2c_master_send(c, &buf, 1); + if (err != 1) { + tveeprom_info("Huh, no eeprom present (err=%d)?\n", err); + return -1; + } + err = i2c_master_recv(c, eedata, len); + if (err != len) { + tveeprom_warn("i2c eeprom read error (err=%d)\n", err); + return -1; + } + if (debug) { + int i; + + tveeprom_info("full 256-byte eeprom dump:\n"); + for (i = 0; i < len; i++) { + if (0 == (i % 16)) + tveeprom_info("%02x:", i); + printk(KERN_CONT " %02x", eedata[i]); + if (15 == (i % 16)) + printk(KERN_CONT "\n"); + } + } + return 0; +} +EXPORT_SYMBOL(tveeprom_read); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c new file mode 100644 index 00000000000..1f3943bb87d --- /dev/null +++ b/drivers/media/i2c/tvp514x.c @@ -0,0 +1,1166 @@ +/* + * drivers/media/i2c/tvp514x.c + * + * TI TVP5146/47 decoder driver + * + * Copyright (C) 2008 Texas Instruments Inc + * Author: Vaibhav Hiremath + * + * Contributors: + * Sivaraj R + * Brijesh R Jadav + * Hardik Shah + * Manjunath Hadli + * Karicheri Muralidharan + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "tvp514x_regs.h" + +/* Module Name */ +#define TVP514X_MODULE_NAME "tvp514x" + +/* Private macros for TVP */ +#define I2C_RETRY_COUNT (5) +#define LOCK_RETRY_COUNT (5) +#define LOCK_RETRY_DELAY (200) + +/* Debug functions */ +static bool debug; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("TVP514X linux decoder driver"); +MODULE_LICENSE("GPL"); + +/* enum tvp514x_std - enum for supported standards */ +enum tvp514x_std { + STD_NTSC_MJ = 0, + STD_PAL_BDGHIN, + STD_INVALID +}; + +/** + * struct tvp514x_std_info - Structure to store standard informations + * @width: Line width in pixels + * @height:Number of active lines + * @video_std: Value to write in REG_VIDEO_STD register + * @standard: v4l2 standard structure information + */ +struct tvp514x_std_info { + unsigned long width; + unsigned long height; + u8 video_std; + struct v4l2_standard standard; +}; + +static struct tvp514x_reg tvp514x_reg_list_default[0x40]; + +static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable); +/** + * struct tvp514x_decoder - TVP5146/47 decoder object + * @sd: Subdevice Slave handle + * @tvp514x_regs: copy of hw's regs with preset values. + * @pdata: Board specific + * @ver: Chip version + * @streaming: TVP5146/47 decoder streaming - enabled or disabled. + * @current_std: Current standard + * @num_stds: Number of standards + * @std_list: Standards list + * @input: Input routing at chip level + * @output: Output routing at chip level + */ +struct tvp514x_decoder { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + struct tvp514x_reg tvp514x_regs[ARRAY_SIZE(tvp514x_reg_list_default)]; + const struct tvp514x_platform_data *pdata; + + int ver; + int streaming; + + enum tvp514x_std current_std; + int num_stds; + const struct tvp514x_std_info *std_list; + /* Input and Output Routing parameters */ + u32 input; + u32 output; +}; + +/* TVP514x default register values */ +static struct tvp514x_reg tvp514x_reg_list_default[] = { + /* Composite selected */ + {TOK_WRITE, REG_INPUT_SEL, 0x05}, + {TOK_WRITE, REG_AFE_GAIN_CTRL, 0x0F}, + /* Auto mode */ + {TOK_WRITE, REG_VIDEO_STD, 0x00}, + {TOK_WRITE, REG_OPERATION_MODE, 0x00}, + {TOK_SKIP, REG_AUTOSWITCH_MASK, 0x3F}, + {TOK_WRITE, REG_COLOR_KILLER, 0x10}, + {TOK_WRITE, REG_LUMA_CONTROL1, 0x00}, + {TOK_WRITE, REG_LUMA_CONTROL2, 0x00}, + {TOK_WRITE, REG_LUMA_CONTROL3, 0x02}, + {TOK_WRITE, REG_BRIGHTNESS, 0x80}, + {TOK_WRITE, REG_CONTRAST, 0x80}, + {TOK_WRITE, REG_SATURATION, 0x80}, + {TOK_WRITE, REG_HUE, 0x00}, + {TOK_WRITE, REG_CHROMA_CONTROL1, 0x00}, + {TOK_WRITE, REG_CHROMA_CONTROL2, 0x0E}, + /* Reserved */ + {TOK_SKIP, 0x0F, 0x00}, + {TOK_WRITE, REG_COMP_PR_SATURATION, 0x80}, + {TOK_WRITE, REG_COMP_Y_CONTRAST, 0x80}, + {TOK_WRITE, REG_COMP_PB_SATURATION, 0x80}, + /* Reserved */ + {TOK_SKIP, 0x13, 0x00}, + {TOK_WRITE, REG_COMP_Y_BRIGHTNESS, 0x80}, + /* Reserved */ + {TOK_SKIP, 0x15, 0x00}, + /* NTSC timing */ + {TOK_SKIP, REG_AVID_START_PIXEL_LSB, 0x55}, + {TOK_SKIP, REG_AVID_START_PIXEL_MSB, 0x00}, + {TOK_SKIP, REG_AVID_STOP_PIXEL_LSB, 0x25}, + {TOK_SKIP, REG_AVID_STOP_PIXEL_MSB, 0x03}, + /* NTSC timing */ + {TOK_SKIP, REG_HSYNC_START_PIXEL_LSB, 0x00}, + {TOK_SKIP, REG_HSYNC_START_PIXEL_MSB, 0x00}, + {TOK_SKIP, REG_HSYNC_STOP_PIXEL_LSB, 0x40}, + {TOK_SKIP, REG_HSYNC_STOP_PIXEL_MSB, 0x00}, + /* NTSC timing */ + {TOK_SKIP, REG_VSYNC_START_LINE_LSB, 0x04}, + {TOK_SKIP, REG_VSYNC_START_LINE_MSB, 0x00}, + {TOK_SKIP, REG_VSYNC_STOP_LINE_LSB, 0x07}, + {TOK_SKIP, REG_VSYNC_STOP_LINE_MSB, 0x00}, + /* NTSC timing */ + {TOK_SKIP, REG_VBLK_START_LINE_LSB, 0x01}, + {TOK_SKIP, REG_VBLK_START_LINE_MSB, 0x00}, + {TOK_SKIP, REG_VBLK_STOP_LINE_LSB, 0x15}, + {TOK_SKIP, REG_VBLK_STOP_LINE_MSB, 0x00}, + /* Reserved */ + {TOK_SKIP, 0x26, 0x00}, + /* Reserved */ + {TOK_SKIP, 0x27, 0x00}, + {TOK_SKIP, REG_FAST_SWTICH_CONTROL, 0xCC}, + /* Reserved */ + {TOK_SKIP, 0x29, 0x00}, + {TOK_SKIP, REG_FAST_SWTICH_SCART_DELAY, 0x00}, + /* Reserved */ + {TOK_SKIP, 0x2B, 0x00}, + {TOK_SKIP, REG_SCART_DELAY, 0x00}, + {TOK_SKIP, REG_CTI_DELAY, 0x00}, + {TOK_SKIP, REG_CTI_CONTROL, 0x00}, + /* Reserved */ + {TOK_SKIP, 0x2F, 0x00}, + /* Reserved */ + {TOK_SKIP, 0x30, 0x00}, + /* Reserved */ + {TOK_SKIP, 0x31, 0x00}, + /* HS, VS active high */ + {TOK_WRITE, REG_SYNC_CONTROL, 0x00}, + /* 10-bit BT.656 */ + {TOK_WRITE, REG_OUTPUT_FORMATTER1, 0x00}, + /* Enable clk & data */ + {TOK_WRITE, REG_OUTPUT_FORMATTER2, 0x11}, + /* Enable AVID & FLD */ + {TOK_WRITE, REG_OUTPUT_FORMATTER3, 0xEE}, + /* Enable VS & HS */ + {TOK_WRITE, REG_OUTPUT_FORMATTER4, 0xAF}, + {TOK_WRITE, REG_OUTPUT_FORMATTER5, 0xFF}, + {TOK_WRITE, REG_OUTPUT_FORMATTER6, 0xFF}, + /* Clear status */ + {TOK_WRITE, REG_CLEAR_LOST_LOCK, 0x01}, + {TOK_TERM, 0, 0}, +}; + +/** + * Supported standards - + * + * Currently supports two standards only, need to add support for rest of the + * modes, like SECAM, etc... + */ +static const struct tvp514x_std_info tvp514x_std_list[] = { + /* Standard: STD_NTSC_MJ */ + [STD_NTSC_MJ] = { + .width = NTSC_NUM_ACTIVE_PIXELS, + .height = NTSC_NUM_ACTIVE_LINES, + .video_std = VIDEO_STD_NTSC_MJ_BIT, + .standard = { + .index = 0, + .id = V4L2_STD_NTSC, + .name = "NTSC", + .frameperiod = {1001, 30000}, + .framelines = 525 + }, + /* Standard: STD_PAL_BDGHIN */ + }, + [STD_PAL_BDGHIN] = { + .width = PAL_NUM_ACTIVE_PIXELS, + .height = PAL_NUM_ACTIVE_LINES, + .video_std = VIDEO_STD_PAL_BDGHIN_BIT, + .standard = { + .index = 1, + .id = V4L2_STD_PAL, + .name = "PAL", + .frameperiod = {1, 25}, + .framelines = 625 + }, + }, + /* Standard: need to add for additional standard */ +}; + + +static inline struct tvp514x_decoder *to_decoder(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tvp514x_decoder, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct tvp514x_decoder, hdl)->sd; +} + + +/** + * tvp514x_read_reg() - Read a value from a register in an TVP5146/47. + * @sd: ptr to v4l2_subdev struct + * @reg: TVP5146/47 register address + * + * Returns value read if successful, or non-zero (-1) otherwise. + */ +static int tvp514x_read_reg(struct v4l2_subdev *sd, u8 reg) +{ + int err, retry = 0; + struct i2c_client *client = v4l2_get_subdevdata(sd); + +read_again: + + err = i2c_smbus_read_byte_data(client, reg); + if (err < 0) { + if (retry <= I2C_RETRY_COUNT) { + v4l2_warn(sd, "Read: retry ... %d\n", retry); + retry++; + msleep_interruptible(10); + goto read_again; + } + } + + return err; +} + +/** + * dump_reg() - dump the register content of TVP5146/47. + * @sd: ptr to v4l2_subdev struct + * @reg: TVP5146/47 register address + */ +static void dump_reg(struct v4l2_subdev *sd, u8 reg) +{ + u32 val; + + val = tvp514x_read_reg(sd, reg); + v4l2_info(sd, "Reg(0x%.2X): 0x%.2X\n", reg, val); +} + +/** + * tvp514x_write_reg() - Write a value to a register in TVP5146/47 + * @sd: ptr to v4l2_subdev struct + * @reg: TVP5146/47 register address + * @val: value to be written to the register + * + * Write a value to a register in an TVP5146/47 decoder device. + * Returns zero if successful, or non-zero otherwise. + */ +static int tvp514x_write_reg(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + int err, retry = 0; + struct i2c_client *client = v4l2_get_subdevdata(sd); + +write_again: + + err = i2c_smbus_write_byte_data(client, reg, val); + if (err) { + if (retry <= I2C_RETRY_COUNT) { + v4l2_warn(sd, "Write: retry ... %d\n", retry); + retry++; + msleep_interruptible(10); + goto write_again; + } + } + + return err; +} + +/** + * tvp514x_write_regs() : Initializes a list of TVP5146/47 registers + * @sd: ptr to v4l2_subdev struct + * @reglist: list of TVP5146/47 registers and values + * + * Initializes a list of TVP5146/47 registers:- + * if token is TOK_TERM, then entire write operation terminates + * if token is TOK_DELAY, then a delay of 'val' msec is introduced + * if token is TOK_SKIP, then the register write is skipped + * if token is TOK_WRITE, then the register write is performed + * Returns zero if successful, or non-zero otherwise. + */ +static int tvp514x_write_regs(struct v4l2_subdev *sd, + const struct tvp514x_reg reglist[]) +{ + int err; + const struct tvp514x_reg *next = reglist; + + for (; next->token != TOK_TERM; next++) { + if (next->token == TOK_DELAY) { + msleep(next->val); + continue; + } + + if (next->token == TOK_SKIP) + continue; + + err = tvp514x_write_reg(sd, next->reg, (u8) next->val); + if (err) { + v4l2_err(sd, "Write failed. Err[%d]\n", err); + return err; + } + } + return 0; +} + +/** + * tvp514x_query_current_std() : Query the current standard detected by TVP5146/47 + * @sd: ptr to v4l2_subdev struct + * + * Returns the current standard detected by TVP5146/47, STD_INVALID if there is no + * standard detected. + */ +static enum tvp514x_std tvp514x_query_current_std(struct v4l2_subdev *sd) +{ + u8 std, std_status; + + std = tvp514x_read_reg(sd, REG_VIDEO_STD); + if ((std & VIDEO_STD_MASK) == VIDEO_STD_AUTO_SWITCH_BIT) + /* use the standard status register */ + std_status = tvp514x_read_reg(sd, REG_VIDEO_STD_STATUS); + else + /* use the standard register itself */ + std_status = std; + + switch (std_status & VIDEO_STD_MASK) { + case VIDEO_STD_NTSC_MJ_BIT: + return STD_NTSC_MJ; + + case VIDEO_STD_PAL_BDGHIN_BIT: + return STD_PAL_BDGHIN; + + default: + return STD_INVALID; + } + + return STD_INVALID; +} + +/* TVP5146/47 register dump function */ +static void tvp514x_reg_dump(struct v4l2_subdev *sd) +{ + dump_reg(sd, REG_INPUT_SEL); + dump_reg(sd, REG_AFE_GAIN_CTRL); + dump_reg(sd, REG_VIDEO_STD); + dump_reg(sd, REG_OPERATION_MODE); + dump_reg(sd, REG_COLOR_KILLER); + dump_reg(sd, REG_LUMA_CONTROL1); + dump_reg(sd, REG_LUMA_CONTROL2); + dump_reg(sd, REG_LUMA_CONTROL3); + dump_reg(sd, REG_BRIGHTNESS); + dump_reg(sd, REG_CONTRAST); + dump_reg(sd, REG_SATURATION); + dump_reg(sd, REG_HUE); + dump_reg(sd, REG_CHROMA_CONTROL1); + dump_reg(sd, REG_CHROMA_CONTROL2); + dump_reg(sd, REG_COMP_PR_SATURATION); + dump_reg(sd, REG_COMP_Y_CONTRAST); + dump_reg(sd, REG_COMP_PB_SATURATION); + dump_reg(sd, REG_COMP_Y_BRIGHTNESS); + dump_reg(sd, REG_AVID_START_PIXEL_LSB); + dump_reg(sd, REG_AVID_START_PIXEL_MSB); + dump_reg(sd, REG_AVID_STOP_PIXEL_LSB); + dump_reg(sd, REG_AVID_STOP_PIXEL_MSB); + dump_reg(sd, REG_HSYNC_START_PIXEL_LSB); + dump_reg(sd, REG_HSYNC_START_PIXEL_MSB); + dump_reg(sd, REG_HSYNC_STOP_PIXEL_LSB); + dump_reg(sd, REG_HSYNC_STOP_PIXEL_MSB); + dump_reg(sd, REG_VSYNC_START_LINE_LSB); + dump_reg(sd, REG_VSYNC_START_LINE_MSB); + dump_reg(sd, REG_VSYNC_STOP_LINE_LSB); + dump_reg(sd, REG_VSYNC_STOP_LINE_MSB); + dump_reg(sd, REG_VBLK_START_LINE_LSB); + dump_reg(sd, REG_VBLK_START_LINE_MSB); + dump_reg(sd, REG_VBLK_STOP_LINE_LSB); + dump_reg(sd, REG_VBLK_STOP_LINE_MSB); + dump_reg(sd, REG_SYNC_CONTROL); + dump_reg(sd, REG_OUTPUT_FORMATTER1); + dump_reg(sd, REG_OUTPUT_FORMATTER2); + dump_reg(sd, REG_OUTPUT_FORMATTER3); + dump_reg(sd, REG_OUTPUT_FORMATTER4); + dump_reg(sd, REG_OUTPUT_FORMATTER5); + dump_reg(sd, REG_OUTPUT_FORMATTER6); + dump_reg(sd, REG_CLEAR_LOST_LOCK); +} + +/** + * tvp514x_configure() - Configure the TVP5146/47 registers + * @sd: ptr to v4l2_subdev struct + * @decoder: ptr to tvp514x_decoder structure + * + * Returns zero if successful, or non-zero otherwise. + */ +static int tvp514x_configure(struct v4l2_subdev *sd, + struct tvp514x_decoder *decoder) +{ + int err; + + /* common register initialization */ + err = + tvp514x_write_regs(sd, decoder->tvp514x_regs); + if (err) + return err; + + if (debug) + tvp514x_reg_dump(sd); + + return 0; +} + +/** + * tvp514x_detect() - Detect if an tvp514x is present, and if so which revision. + * @sd: pointer to standard V4L2 sub-device structure + * @decoder: pointer to tvp514x_decoder structure + * + * A device is considered to be detected if the chip ID (LSB and MSB) + * registers match the expected values. + * Any value of the rom version register is accepted. + * Returns ENODEV error number if no device is detected, or zero + * if a device is detected. + */ +static int tvp514x_detect(struct v4l2_subdev *sd, + struct tvp514x_decoder *decoder) +{ + u8 chip_id_msb, chip_id_lsb, rom_ver; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + chip_id_msb = tvp514x_read_reg(sd, REG_CHIP_ID_MSB); + chip_id_lsb = tvp514x_read_reg(sd, REG_CHIP_ID_LSB); + rom_ver = tvp514x_read_reg(sd, REG_ROM_VERSION); + + v4l2_dbg(1, debug, sd, + "chip id detected msb:0x%x lsb:0x%x rom version:0x%x\n", + chip_id_msb, chip_id_lsb, rom_ver); + if ((chip_id_msb != TVP514X_CHIP_ID_MSB) + || ((chip_id_lsb != TVP5146_CHIP_ID_LSB) + && (chip_id_lsb != TVP5147_CHIP_ID_LSB))) { + /* We didn't read the values we expected, so this must not be + * an TVP5146/47. + */ + v4l2_err(sd, "chip id mismatch msb:0x%x lsb:0x%x\n", + chip_id_msb, chip_id_lsb); + return -ENODEV; + } + + decoder->ver = rom_ver; + + v4l2_info(sd, "%s (Version - 0x%.2x) found at 0x%x (%s)\n", + client->name, decoder->ver, + client->addr << 1, client->adapter->name); + return 0; +} + +/** + * tvp514x_querystd() - V4L2 decoder interface handler for querystd + * @sd: pointer to standard V4L2 sub-device structure + * @std_id: standard V4L2 std_id ioctl enum + * + * Returns the current standard detected by TVP5146/47. If no active input is + * detected then *std_id is set to 0 and the function returns 0. + */ +static int tvp514x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std_id) +{ + struct tvp514x_decoder *decoder = to_decoder(sd); + enum tvp514x_std current_std; + enum tvp514x_input input_sel; + u8 sync_lock_status, lock_mask; + + if (std_id == NULL) + return -EINVAL; + + *std_id = V4L2_STD_UNKNOWN; + + /* query the current standard */ + current_std = tvp514x_query_current_std(sd); + if (current_std == STD_INVALID) + return 0; + + input_sel = decoder->input; + + switch (input_sel) { + case INPUT_CVBS_VI1A: + case INPUT_CVBS_VI1B: + case INPUT_CVBS_VI1C: + case INPUT_CVBS_VI2A: + case INPUT_CVBS_VI2B: + case INPUT_CVBS_VI2C: + case INPUT_CVBS_VI3A: + case INPUT_CVBS_VI3B: + case INPUT_CVBS_VI3C: + case INPUT_CVBS_VI4A: + lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT | + STATUS_HORZ_SYNC_LOCK_BIT | + STATUS_VIRT_SYNC_LOCK_BIT; + break; + + case INPUT_SVIDEO_VI2A_VI1A: + case INPUT_SVIDEO_VI2B_VI1B: + case INPUT_SVIDEO_VI2C_VI1C: + case INPUT_SVIDEO_VI2A_VI3A: + case INPUT_SVIDEO_VI2B_VI3B: + case INPUT_SVIDEO_VI2C_VI3C: + case INPUT_SVIDEO_VI4A_VI1A: + case INPUT_SVIDEO_VI4A_VI1B: + case INPUT_SVIDEO_VI4A_VI1C: + case INPUT_SVIDEO_VI4A_VI3A: + case INPUT_SVIDEO_VI4A_VI3B: + case INPUT_SVIDEO_VI4A_VI3C: + lock_mask = STATUS_HORZ_SYNC_LOCK_BIT | + STATUS_VIRT_SYNC_LOCK_BIT; + break; + /*Need to add other interfaces*/ + default: + return -EINVAL; + } + /* check whether signal is locked */ + sync_lock_status = tvp514x_read_reg(sd, REG_STATUS1); + if (lock_mask != (sync_lock_status & lock_mask)) + return 0; /* No input detected */ + + *std_id = decoder->std_list[current_std].standard.id; + + v4l2_dbg(1, debug, sd, "Current STD: %s\n", + decoder->std_list[current_std].standard.name); + return 0; +} + +/** + * tvp514x_s_std() - V4L2 decoder interface handler for s_std + * @sd: pointer to standard V4L2 sub-device structure + * @std_id: standard V4L2 v4l2_std_id ioctl enum + * + * If std_id is supported, sets the requested standard. Otherwise, returns + * -EINVAL + */ +static int tvp514x_s_std(struct v4l2_subdev *sd, v4l2_std_id std_id) +{ + struct tvp514x_decoder *decoder = to_decoder(sd); + int err, i; + + for (i = 0; i < decoder->num_stds; i++) + if (std_id & decoder->std_list[i].standard.id) + break; + + if ((i == decoder->num_stds) || (i == STD_INVALID)) + return -EINVAL; + + err = tvp514x_write_reg(sd, REG_VIDEO_STD, + decoder->std_list[i].video_std); + if (err) + return err; + + decoder->current_std = i; + decoder->tvp514x_regs[REG_VIDEO_STD].val = + decoder->std_list[i].video_std; + + v4l2_dbg(1, debug, sd, "Standard set to: %s\n", + decoder->std_list[i].standard.name); + return 0; +} + +/** + * tvp514x_s_routing() - V4L2 decoder interface handler for s_routing + * @sd: pointer to standard V4L2 sub-device structure + * @input: input selector for routing the signal + * @output: output selector for routing the signal + * @config: config value. Not used + * + * If index is valid, selects the requested input. Otherwise, returns -EINVAL if + * the input is not supported or there is no active signal present in the + * selected input. + */ +static int tvp514x_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct tvp514x_decoder *decoder = to_decoder(sd); + int err; + enum tvp514x_input input_sel; + enum tvp514x_output output_sel; + u8 sync_lock_status, lock_mask; + int try_count = LOCK_RETRY_COUNT; + + if ((input >= INPUT_INVALID) || + (output >= OUTPUT_INVALID)) + /* Index out of bound */ + return -EINVAL; + + /* + * For the sequence streamon -> streamoff and again s_input + * it fails to lock the signal, since streamoff puts TVP514x + * into power off state which leads to failure in sub-sequent s_input. + * + * So power up the TVP514x device here, since it is important to lock + * the signal at this stage. + */ + if (!decoder->streaming) + tvp514x_s_stream(sd, 1); + + input_sel = input; + output_sel = output; + + err = tvp514x_write_reg(sd, REG_INPUT_SEL, input_sel); + if (err) + return err; + + output_sel |= tvp514x_read_reg(sd, + REG_OUTPUT_FORMATTER1) & 0x7; + err = tvp514x_write_reg(sd, REG_OUTPUT_FORMATTER1, + output_sel); + if (err) + return err; + + decoder->tvp514x_regs[REG_INPUT_SEL].val = input_sel; + decoder->tvp514x_regs[REG_OUTPUT_FORMATTER1].val = output_sel; + + /* Clear status */ + msleep(LOCK_RETRY_DELAY); + err = + tvp514x_write_reg(sd, REG_CLEAR_LOST_LOCK, 0x01); + if (err) + return err; + + switch (input_sel) { + case INPUT_CVBS_VI1A: + case INPUT_CVBS_VI1B: + case INPUT_CVBS_VI1C: + case INPUT_CVBS_VI2A: + case INPUT_CVBS_VI2B: + case INPUT_CVBS_VI2C: + case INPUT_CVBS_VI3A: + case INPUT_CVBS_VI3B: + case INPUT_CVBS_VI3C: + case INPUT_CVBS_VI4A: + lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT | + STATUS_HORZ_SYNC_LOCK_BIT | + STATUS_VIRT_SYNC_LOCK_BIT; + break; + + case INPUT_SVIDEO_VI2A_VI1A: + case INPUT_SVIDEO_VI2B_VI1B: + case INPUT_SVIDEO_VI2C_VI1C: + case INPUT_SVIDEO_VI2A_VI3A: + case INPUT_SVIDEO_VI2B_VI3B: + case INPUT_SVIDEO_VI2C_VI3C: + case INPUT_SVIDEO_VI4A_VI1A: + case INPUT_SVIDEO_VI4A_VI1B: + case INPUT_SVIDEO_VI4A_VI1C: + case INPUT_SVIDEO_VI4A_VI3A: + case INPUT_SVIDEO_VI4A_VI3B: + case INPUT_SVIDEO_VI4A_VI3C: + lock_mask = STATUS_HORZ_SYNC_LOCK_BIT | + STATUS_VIRT_SYNC_LOCK_BIT; + break; + /* Need to add other interfaces*/ + default: + return -EINVAL; + } + + while (try_count-- > 0) { + /* Allow decoder to sync up with new input */ + msleep(LOCK_RETRY_DELAY); + + sync_lock_status = tvp514x_read_reg(sd, + REG_STATUS1); + if (lock_mask == (sync_lock_status & lock_mask)) + /* Input detected */ + break; + } + + if (try_count < 0) + return -EINVAL; + + decoder->input = input; + decoder->output = output; + + v4l2_dbg(1, debug, sd, "Input set to: %d\n", input_sel); + + return 0; +} + +/** + * tvp514x_s_ctrl() - V4L2 decoder interface handler for s_ctrl + * @ctrl: pointer to v4l2_ctrl structure + * + * If the requested control is supported, sets the control's current + * value in HW. Otherwise, returns -EINVAL if the control is not supported. + */ +static int tvp514x_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct tvp514x_decoder *decoder = to_decoder(sd); + int err = -EINVAL, value; + + value = ctrl->val; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + err = tvp514x_write_reg(sd, REG_BRIGHTNESS, value); + if (!err) + decoder->tvp514x_regs[REG_BRIGHTNESS].val = value; + break; + case V4L2_CID_CONTRAST: + err = tvp514x_write_reg(sd, REG_CONTRAST, value); + if (!err) + decoder->tvp514x_regs[REG_CONTRAST].val = value; + break; + case V4L2_CID_SATURATION: + err = tvp514x_write_reg(sd, REG_SATURATION, value); + if (!err) + decoder->tvp514x_regs[REG_SATURATION].val = value; + break; + case V4L2_CID_HUE: + if (value == 180) + value = 0x7F; + else if (value == -180) + value = 0x80; + err = tvp514x_write_reg(sd, REG_HUE, value); + if (!err) + decoder->tvp514x_regs[REG_HUE].val = value; + break; + case V4L2_CID_AUTOGAIN: + err = tvp514x_write_reg(sd, REG_AFE_GAIN_CTRL, value ? 0x0f : 0x0c); + if (!err) + decoder->tvp514x_regs[REG_AFE_GAIN_CTRL].val = value; + break; + } + + v4l2_dbg(1, debug, sd, "Set Control: ID - %d - %d\n", + ctrl->id, ctrl->val); + return err; +} + +/** + * tvp514x_enum_mbus_fmt() - V4L2 decoder interface handler for enum_mbus_fmt + * @sd: pointer to standard V4L2 sub-device structure + * @index: index of pixelcode to retrieve + * @code: receives the pixelcode + * + * Enumerates supported mediabus formats + */ +static int +tvp514x_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, + enum v4l2_mbus_pixelcode *code) +{ + if (index) + return -EINVAL; + + *code = V4L2_MBUS_FMT_YUYV10_2X10; + return 0; +} + +/** + * tvp514x_mbus_fmt_cap() - V4L2 decoder interface handler for try/s/g_mbus_fmt + * @sd: pointer to standard V4L2 sub-device structure + * @f: pointer to the mediabus format structure + * + * Negotiates the image capture size and mediabus format. + */ +static int +tvp514x_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) +{ + struct tvp514x_decoder *decoder = to_decoder(sd); + enum tvp514x_std current_std; + + if (f == NULL) + return -EINVAL; + + /* Calculate height and width based on current standard */ + current_std = decoder->current_std; + + f->code = V4L2_MBUS_FMT_YUYV10_2X10; + f->width = decoder->std_list[current_std].width; + f->height = decoder->std_list[current_std].height; + f->field = V4L2_FIELD_INTERLACED; + f->colorspace = V4L2_COLORSPACE_SMPTE170M; + + v4l2_dbg(1, debug, sd, "MBUS_FMT: Width - %d, Height - %d\n", + f->width, f->height); + return 0; +} + +/** + * tvp514x_g_parm() - V4L2 decoder interface handler for g_parm + * @sd: pointer to standard V4L2 sub-device structure + * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure + * + * Returns the decoder's video CAPTURE parameters. + */ +static int +tvp514x_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a) +{ + struct tvp514x_decoder *decoder = to_decoder(sd); + struct v4l2_captureparm *cparm; + enum tvp514x_std current_std; + + if (a == NULL) + return -EINVAL; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + /* only capture is supported */ + return -EINVAL; + + /* get the current standard */ + current_std = decoder->current_std; + + cparm = &a->parm.capture; + cparm->capability = V4L2_CAP_TIMEPERFRAME; + cparm->timeperframe = + decoder->std_list[current_std].standard.frameperiod; + + return 0; +} + +/** + * tvp514x_s_parm() - V4L2 decoder interface handler for s_parm + * @sd: pointer to standard V4L2 sub-device structure + * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure + * + * Configures the decoder to use the input parameters, if possible. If + * not possible, returns the appropriate error code. + */ +static int +tvp514x_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a) +{ + struct tvp514x_decoder *decoder = to_decoder(sd); + struct v4l2_fract *timeperframe; + enum tvp514x_std current_std; + + if (a == NULL) + return -EINVAL; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + /* only capture is supported */ + return -EINVAL; + + timeperframe = &a->parm.capture.timeperframe; + + /* get the current standard */ + current_std = decoder->current_std; + + *timeperframe = + decoder->std_list[current_std].standard.frameperiod; + + return 0; +} + +/** + * tvp514x_s_stream() - V4L2 decoder i/f handler for s_stream + * @sd: pointer to standard V4L2 sub-device structure + * @enable: streaming enable or disable + * + * Sets streaming to enable or disable, if possible. + */ +static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable) +{ + int err = 0; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct tvp514x_decoder *decoder = to_decoder(sd); + + if (decoder->streaming == enable) + return 0; + + switch (enable) { + case 0: + { + /* Power Down Sequence */ + err = tvp514x_write_reg(sd, REG_OPERATION_MODE, 0x01); + if (err) { + v4l2_err(sd, "Unable to turn off decoder\n"); + return err; + } + decoder->streaming = enable; + break; + } + case 1: + { + struct tvp514x_reg *int_seq = (struct tvp514x_reg *) + client->driver->id_table->driver_data; + + /* Power Up Sequence */ + err = tvp514x_write_regs(sd, int_seq); + if (err) { + v4l2_err(sd, "Unable to turn on decoder\n"); + return err; + } + /* Detect if not already detected */ + err = tvp514x_detect(sd, decoder); + if (err) { + v4l2_err(sd, "Unable to detect decoder\n"); + return err; + } + err = tvp514x_configure(sd, decoder); + if (err) { + v4l2_err(sd, "Unable to configure decoder\n"); + return err; + } + decoder->streaming = enable; + break; + } + default: + err = -ENODEV; + break; + } + + return err; +} + +static const struct v4l2_ctrl_ops tvp514x_ctrl_ops = { + .s_ctrl = tvp514x_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops tvp514x_core_ops = { + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, + .s_std = tvp514x_s_std, +}; + +static const struct v4l2_subdev_video_ops tvp514x_video_ops = { + .s_routing = tvp514x_s_routing, + .querystd = tvp514x_querystd, + .enum_mbus_fmt = tvp514x_enum_mbus_fmt, + .g_mbus_fmt = tvp514x_mbus_fmt, + .try_mbus_fmt = tvp514x_mbus_fmt, + .s_mbus_fmt = tvp514x_mbus_fmt, + .g_parm = tvp514x_g_parm, + .s_parm = tvp514x_s_parm, + .s_stream = tvp514x_s_stream, +}; + +static const struct v4l2_subdev_ops tvp514x_ops = { + .core = &tvp514x_core_ops, + .video = &tvp514x_video_ops, +}; + +static struct tvp514x_decoder tvp514x_dev = { + .streaming = 0, + .current_std = STD_NTSC_MJ, + .std_list = tvp514x_std_list, + .num_stds = ARRAY_SIZE(tvp514x_std_list), + +}; + +/** + * tvp514x_probe() - decoder driver i2c probe handler + * @client: i2c driver client device structure + * @id: i2c driver id table + * + * Register decoder as an i2c client device and V4L2 + * device. + */ +static int +tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct tvp514x_decoder *decoder; + struct v4l2_subdev *sd; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + if (!client->dev.platform_data) { + v4l2_err(client, "No platform data!!\n"); + return -ENODEV; + } + + decoder = kzalloc(sizeof(*decoder), GFP_KERNEL); + if (!decoder) + return -ENOMEM; + + /* Initialize the tvp514x_decoder with default configuration */ + *decoder = tvp514x_dev; + /* Copy default register configuration */ + memcpy(decoder->tvp514x_regs, tvp514x_reg_list_default, + sizeof(tvp514x_reg_list_default)); + + /* Copy board specific information here */ + decoder->pdata = client->dev.platform_data; + + /** + * Fetch platform specific data, and configure the + * tvp514x_reg_list[] accordingly. Since this is one + * time configuration, no need to preserve. + */ + decoder->tvp514x_regs[REG_OUTPUT_FORMATTER2].val |= + (decoder->pdata->clk_polarity << 1); + decoder->tvp514x_regs[REG_SYNC_CONTROL].val |= + ((decoder->pdata->hs_polarity << 2) | + (decoder->pdata->vs_polarity << 3)); + /* Set default standard to auto */ + decoder->tvp514x_regs[REG_VIDEO_STD].val = + VIDEO_STD_AUTO_SWITCH_BIT; + + /* Register with V4L2 layer as slave device */ + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &tvp514x_ops); + + v4l2_ctrl_handler_init(&decoder->hdl, 5); + v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 128); + v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 128); + v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, + V4L2_CID_HUE, -180, 180, 180, 0); + v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + sd->ctrl_handler = &decoder->hdl; + if (decoder->hdl.error) { + int err = decoder->hdl.error; + + v4l2_ctrl_handler_free(&decoder->hdl); + kfree(decoder); + return err; + } + v4l2_ctrl_handler_setup(&decoder->hdl); + + v4l2_info(sd, "%s decoder driver registered !!\n", sd->name); + + return 0; + +} + +/** + * tvp514x_remove() - decoder driver i2c remove handler + * @client: i2c driver client device structure + * + * Unregister decoder as an i2c client device and V4L2 + * device. Complement of tvp514x_probe(). + */ +static int tvp514x_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct tvp514x_decoder *decoder = to_decoder(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&decoder->hdl); + kfree(decoder); + return 0; +} +/* TVP5146 Init/Power on Sequence */ +static const struct tvp514x_reg tvp5146_init_reg_seq[] = { + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00}, + {TOK_WRITE, REG_OPERATION_MODE, 0x01}, + {TOK_WRITE, REG_OPERATION_MODE, 0x00}, + {TOK_TERM, 0, 0}, +}; + +/* TVP5147 Init/Power on Sequence */ +static const struct tvp514x_reg tvp5147_init_reg_seq[] = { + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x16}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xA0}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x16}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00}, + {TOK_WRITE, REG_OPERATION_MODE, 0x01}, + {TOK_WRITE, REG_OPERATION_MODE, 0x00}, + {TOK_TERM, 0, 0}, +}; + +/* TVP5146M2/TVP5147M1 Init/Power on Sequence */ +static const struct tvp514x_reg tvp514xm_init_reg_seq[] = { + {TOK_WRITE, REG_OPERATION_MODE, 0x01}, + {TOK_WRITE, REG_OPERATION_MODE, 0x00}, + {TOK_TERM, 0, 0}, +}; + +/** + * I2C Device Table - + * + * name - Name of the actual device/chip. + * driver_data - Driver data + */ +static const struct i2c_device_id tvp514x_id[] = { + {"tvp5146", (unsigned long)tvp5146_init_reg_seq}, + {"tvp5146m2", (unsigned long)tvp514xm_init_reg_seq}, + {"tvp5147", (unsigned long)tvp5147_init_reg_seq}, + {"tvp5147m1", (unsigned long)tvp514xm_init_reg_seq}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, tvp514x_id); + +static struct i2c_driver tvp514x_driver = { + .driver = { + .owner = THIS_MODULE, + .name = TVP514X_MODULE_NAME, + }, + .probe = tvp514x_probe, + .remove = tvp514x_remove, + .id_table = tvp514x_id, +}; + +module_i2c_driver(tvp514x_driver); diff --git a/drivers/media/i2c/tvp514x_regs.h b/drivers/media/i2c/tvp514x_regs.h new file mode 100644 index 00000000000..d23aa2fbb9b --- /dev/null +++ b/drivers/media/i2c/tvp514x_regs.h @@ -0,0 +1,287 @@ +/* + * drivers/media/i2c/tvp514x_regs.h + * + * Copyright (C) 2008 Texas Instruments Inc + * Author: Vaibhav Hiremath + * + * Contributors: + * Sivaraj R + * Brijesh R Jadav + * Hardik Shah + * Manjunath Hadli + * Karicheri Muralidharan + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + */ + +#ifndef _TVP514X_REGS_H +#define _TVP514X_REGS_H + +/* + * TVP5146/47 registers + */ +#define REG_INPUT_SEL (0x00) +#define REG_AFE_GAIN_CTRL (0x01) +#define REG_VIDEO_STD (0x02) +#define REG_OPERATION_MODE (0x03) +#define REG_AUTOSWITCH_MASK (0x04) + +#define REG_COLOR_KILLER (0x05) +#define REG_LUMA_CONTROL1 (0x06) +#define REG_LUMA_CONTROL2 (0x07) +#define REG_LUMA_CONTROL3 (0x08) + +#define REG_BRIGHTNESS (0x09) +#define REG_CONTRAST (0x0A) +#define REG_SATURATION (0x0B) +#define REG_HUE (0x0C) + +#define REG_CHROMA_CONTROL1 (0x0D) +#define REG_CHROMA_CONTROL2 (0x0E) + +/* 0x0F Reserved */ + +#define REG_COMP_PR_SATURATION (0x10) +#define REG_COMP_Y_CONTRAST (0x11) +#define REG_COMP_PB_SATURATION (0x12) + +/* 0x13 Reserved */ + +#define REG_COMP_Y_BRIGHTNESS (0x14) + +/* 0x15 Reserved */ + +#define REG_AVID_START_PIXEL_LSB (0x16) +#define REG_AVID_START_PIXEL_MSB (0x17) +#define REG_AVID_STOP_PIXEL_LSB (0x18) +#define REG_AVID_STOP_PIXEL_MSB (0x19) + +#define REG_HSYNC_START_PIXEL_LSB (0x1A) +#define REG_HSYNC_START_PIXEL_MSB (0x1B) +#define REG_HSYNC_STOP_PIXEL_LSB (0x1C) +#define REG_HSYNC_STOP_PIXEL_MSB (0x1D) + +#define REG_VSYNC_START_LINE_LSB (0x1E) +#define REG_VSYNC_START_LINE_MSB (0x1F) +#define REG_VSYNC_STOP_LINE_LSB (0x20) +#define REG_VSYNC_STOP_LINE_MSB (0x21) + +#define REG_VBLK_START_LINE_LSB (0x22) +#define REG_VBLK_START_LINE_MSB (0x23) +#define REG_VBLK_STOP_LINE_LSB (0x24) +#define REG_VBLK_STOP_LINE_MSB (0x25) + +/* 0x26 - 0x27 Reserved */ + +#define REG_FAST_SWTICH_CONTROL (0x28) + +/* 0x29 Reserved */ + +#define REG_FAST_SWTICH_SCART_DELAY (0x2A) + +/* 0x2B Reserved */ + +#define REG_SCART_DELAY (0x2C) +#define REG_CTI_DELAY (0x2D) +#define REG_CTI_CONTROL (0x2E) + +/* 0x2F - 0x31 Reserved */ + +#define REG_SYNC_CONTROL (0x32) +#define REG_OUTPUT_FORMATTER1 (0x33) +#define REG_OUTPUT_FORMATTER2 (0x34) +#define REG_OUTPUT_FORMATTER3 (0x35) +#define REG_OUTPUT_FORMATTER4 (0x36) +#define REG_OUTPUT_FORMATTER5 (0x37) +#define REG_OUTPUT_FORMATTER6 (0x38) +#define REG_CLEAR_LOST_LOCK (0x39) + +#define REG_STATUS1 (0x3A) +#define REG_STATUS2 (0x3B) + +#define REG_AGC_GAIN_STATUS_LSB (0x3C) +#define REG_AGC_GAIN_STATUS_MSB (0x3D) + +/* 0x3E Reserved */ + +#define REG_VIDEO_STD_STATUS (0x3F) +#define REG_GPIO_INPUT1 (0x40) +#define REG_GPIO_INPUT2 (0x41) + +/* 0x42 - 0x45 Reserved */ + +#define REG_AFE_COARSE_GAIN_CH1 (0x46) +#define REG_AFE_COARSE_GAIN_CH2 (0x47) +#define REG_AFE_COARSE_GAIN_CH3 (0x48) +#define REG_AFE_COARSE_GAIN_CH4 (0x49) + +#define REG_AFE_FINE_GAIN_PB_B_LSB (0x4A) +#define REG_AFE_FINE_GAIN_PB_B_MSB (0x4B) +#define REG_AFE_FINE_GAIN_Y_G_CHROMA_LSB (0x4C) +#define REG_AFE_FINE_GAIN_Y_G_CHROMA_MSB (0x4D) +#define REG_AFE_FINE_GAIN_PR_R_LSB (0x4E) +#define REG_AFE_FINE_GAIN_PR_R_MSB (0x4F) +#define REG_AFE_FINE_GAIN_CVBS_LUMA_LSB (0x50) +#define REG_AFE_FINE_GAIN_CVBS_LUMA_MSB (0x51) + +/* 0x52 - 0x68 Reserved */ + +#define REG_FBIT_VBIT_CONTROL1 (0x69) + +/* 0x6A - 0x6B Reserved */ + +#define REG_BACKEND_AGC_CONTROL (0x6C) + +/* 0x6D - 0x6E Reserved */ + +#define REG_AGC_DECREMENT_SPEED_CONTROL (0x6F) +#define REG_ROM_VERSION (0x70) + +/* 0x71 - 0x73 Reserved */ + +#define REG_AGC_WHITE_PEAK_PROCESSING (0x74) +#define REG_FBIT_VBIT_CONTROL2 (0x75) +#define REG_VCR_TRICK_MODE_CONTROL (0x76) +#define REG_HORIZONTAL_SHAKE_INCREMENT (0x77) +#define REG_AGC_INCREMENT_SPEED (0x78) +#define REG_AGC_INCREMENT_DELAY (0x79) + +/* 0x7A - 0x7F Reserved */ + +#define REG_CHIP_ID_MSB (0x80) +#define REG_CHIP_ID_LSB (0x81) + +/* 0x82 Reserved */ + +#define REG_CPLL_SPEED_CONTROL (0x83) + +/* 0x84 - 0x96 Reserved */ + +#define REG_STATUS_REQUEST (0x97) + +/* 0x98 - 0x99 Reserved */ + +#define REG_VERTICAL_LINE_COUNT_LSB (0x9A) +#define REG_VERTICAL_LINE_COUNT_MSB (0x9B) + +/* 0x9C - 0x9D Reserved */ + +#define REG_AGC_DECREMENT_DELAY (0x9E) + +/* 0x9F - 0xB0 Reserved */ + +#define REG_VDP_TTX_FILTER_1_MASK1 (0xB1) +#define REG_VDP_TTX_FILTER_1_MASK2 (0xB2) +#define REG_VDP_TTX_FILTER_1_MASK3 (0xB3) +#define REG_VDP_TTX_FILTER_1_MASK4 (0xB4) +#define REG_VDP_TTX_FILTER_1_MASK5 (0xB5) +#define REG_VDP_TTX_FILTER_2_MASK1 (0xB6) +#define REG_VDP_TTX_FILTER_2_MASK2 (0xB7) +#define REG_VDP_TTX_FILTER_2_MASK3 (0xB8) +#define REG_VDP_TTX_FILTER_2_MASK4 (0xB9) +#define REG_VDP_TTX_FILTER_2_MASK5 (0xBA) +#define REG_VDP_TTX_FILTER_CONTROL (0xBB) +#define REG_VDP_FIFO_WORD_COUNT (0xBC) +#define REG_VDP_FIFO_INTERRUPT_THRLD (0xBD) + +/* 0xBE Reserved */ + +#define REG_VDP_FIFO_RESET (0xBF) +#define REG_VDP_FIFO_OUTPUT_CONTROL (0xC0) +#define REG_VDP_LINE_NUMBER_INTERRUPT (0xC1) +#define REG_VDP_PIXEL_ALIGNMENT_LSB (0xC2) +#define REG_VDP_PIXEL_ALIGNMENT_MSB (0xC3) + +/* 0xC4 - 0xD5 Reserved */ + +#define REG_VDP_LINE_START (0xD6) +#define REG_VDP_LINE_STOP (0xD7) +#define REG_VDP_GLOBAL_LINE_MODE (0xD8) +#define REG_VDP_FULL_FIELD_ENABLE (0xD9) +#define REG_VDP_FULL_FIELD_MODE (0xDA) + +/* 0xDB - 0xDF Reserved */ + +#define REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR (0xE0) +#define REG_VBUS_DATA_ACCESS_VBUS_ADDR_INCR (0xE1) +#define REG_FIFO_READ_DATA (0xE2) + +/* 0xE3 - 0xE7 Reserved */ + +#define REG_VBUS_ADDRESS_ACCESS1 (0xE8) +#define REG_VBUS_ADDRESS_ACCESS2 (0xE9) +#define REG_VBUS_ADDRESS_ACCESS3 (0xEA) + +/* 0xEB - 0xEF Reserved */ + +#define REG_INTERRUPT_RAW_STATUS0 (0xF0) +#define REG_INTERRUPT_RAW_STATUS1 (0xF1) +#define REG_INTERRUPT_STATUS0 (0xF2) +#define REG_INTERRUPT_STATUS1 (0xF3) +#define REG_INTERRUPT_MASK0 (0xF4) +#define REG_INTERRUPT_MASK1 (0xF5) +#define REG_INTERRUPT_CLEAR0 (0xF6) +#define REG_INTERRUPT_CLEAR1 (0xF7) + +/* 0xF8 - 0xFF Reserved */ + +/* + * Mask and bit definitions of TVP5146/47 registers + */ +/* The ID values we are looking for */ +#define TVP514X_CHIP_ID_MSB (0x51) +#define TVP5146_CHIP_ID_LSB (0x46) +#define TVP5147_CHIP_ID_LSB (0x47) + +#define VIDEO_STD_MASK (0x07) +#define VIDEO_STD_AUTO_SWITCH_BIT (0x00) +#define VIDEO_STD_NTSC_MJ_BIT (0x01) +#define VIDEO_STD_PAL_BDGHIN_BIT (0x02) +#define VIDEO_STD_PAL_M_BIT (0x03) +#define VIDEO_STD_PAL_COMBINATION_N_BIT (0x04) +#define VIDEO_STD_NTSC_4_43_BIT (0x05) +#define VIDEO_STD_SECAM_BIT (0x06) +#define VIDEO_STD_PAL_60_BIT (0x07) + +/* + * Status bit + */ +#define STATUS_TV_VCR_BIT (1<<0) +#define STATUS_HORZ_SYNC_LOCK_BIT (1<<1) +#define STATUS_VIRT_SYNC_LOCK_BIT (1<<2) +#define STATUS_CLR_SUBCAR_LOCK_BIT (1<<3) +#define STATUS_LOST_LOCK_DETECT_BIT (1<<4) +#define STATUS_FEILD_RATE_BIT (1<<5) +#define STATUS_LINE_ALTERNATING_BIT (1<<6) +#define STATUS_PEAK_WHITE_DETECT_BIT (1<<7) + +/* Tokens for register write */ +#define TOK_WRITE (0) /* token for write operation */ +#define TOK_TERM (1) /* terminating token */ +#define TOK_DELAY (2) /* delay token for reg list */ +#define TOK_SKIP (3) /* token to skip a register */ +/** + * struct tvp514x_reg - Structure for TVP5146/47 register initialization values + * @token - Token: TOK_WRITE, TOK_TERM etc.. + * @reg - Register offset + * @val - Register Value for TOK_WRITE or delay in ms for TOK_DELAY + */ +struct tvp514x_reg { + u8 token; + u8 reg; + u32 val; +}; + +#endif /* ifndef _TVP514X_REGS_H */ diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c new file mode 100644 index 00000000000..a751b6c146f --- /dev/null +++ b/drivers/media/i2c/tvp5150.c @@ -0,0 +1,1274 @@ +/* + * tvp5150 - Texas Instruments TVP5150A/AM1 video decoder driver + * + * Copyright (c) 2005,2006 Mauro Carvalho Chehab (mchehab@infradead.org) + * This code is placed under the terms of the GNU General Public License v2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tvp5150_reg.h" + +#define TVP5150_H_MAX 720 +#define TVP5150_V_MAX_525_60 480 +#define TVP5150_V_MAX_OTHERS 576 +#define TVP5150_MAX_CROP_LEFT 511 +#define TVP5150_MAX_CROP_TOP 127 +#define TVP5150_CROP_SHIFT 2 + +MODULE_DESCRIPTION("Texas Instruments TVP5150A video decoder driver"); +MODULE_AUTHOR("Mauro Carvalho Chehab"); +MODULE_LICENSE("GPL"); + + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-2)"); + +struct tvp5150 { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + struct v4l2_rect rect; + + v4l2_std_id norm; /* Current set standard */ + u32 input; + u32 output; + int enable; +}; + +static inline struct tvp5150 *to_tvp5150(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tvp5150, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct tvp5150, hdl)->sd; +} + +static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr) +{ + struct i2c_client *c = v4l2_get_subdevdata(sd); + unsigned char buffer[1]; + int rc; + + buffer[0] = addr; + + rc = i2c_master_send(c, buffer, 1); + if (rc < 0) { + v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc); + return rc; + } + + msleep(10); + + rc = i2c_master_recv(c, buffer, 1); + if (rc < 0) { + v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc); + return rc; + } + + v4l2_dbg(2, debug, sd, "tvp5150: read 0x%02x = 0x%02x\n", addr, buffer[0]); + + return (buffer[0]); +} + +static inline void tvp5150_write(struct v4l2_subdev *sd, unsigned char addr, + unsigned char value) +{ + struct i2c_client *c = v4l2_get_subdevdata(sd); + unsigned char buffer[2]; + int rc; + + buffer[0] = addr; + buffer[1] = value; + v4l2_dbg(2, debug, sd, "tvp5150: writing 0x%02x 0x%02x\n", buffer[0], buffer[1]); + if (2 != (rc = i2c_master_send(c, buffer, 2))) + v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 2)\n", rc); +} + +static void dump_reg_range(struct v4l2_subdev *sd, char *s, u8 init, + const u8 end, int max_line) +{ + int i = 0; + + while (init != (u8)(end + 1)) { + if ((i % max_line) == 0) { + if (i > 0) + printk("\n"); + printk("tvp5150: %s reg 0x%02x = ", s, init); + } + printk("%02x ", tvp5150_read(sd, init)); + + init++; + i++; + } + printk("\n"); +} + +static int tvp5150_log_status(struct v4l2_subdev *sd) +{ + printk("tvp5150: Video input source selection #1 = 0x%02x\n", + tvp5150_read(sd, TVP5150_VD_IN_SRC_SEL_1)); + printk("tvp5150: Analog channel controls = 0x%02x\n", + tvp5150_read(sd, TVP5150_ANAL_CHL_CTL)); + printk("tvp5150: Operation mode controls = 0x%02x\n", + tvp5150_read(sd, TVP5150_OP_MODE_CTL)); + printk("tvp5150: Miscellaneous controls = 0x%02x\n", + tvp5150_read(sd, TVP5150_MISC_CTL)); + printk("tvp5150: Autoswitch mask= 0x%02x\n", + tvp5150_read(sd, TVP5150_AUTOSW_MSK)); + printk("tvp5150: Color killer threshold control = 0x%02x\n", + tvp5150_read(sd, TVP5150_COLOR_KIL_THSH_CTL)); + printk("tvp5150: Luminance processing controls #1 #2 and #3 = %02x %02x %02x\n", + tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_1), + tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_2), + tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_3)); + printk("tvp5150: Brightness control = 0x%02x\n", + tvp5150_read(sd, TVP5150_BRIGHT_CTL)); + printk("tvp5150: Color saturation control = 0x%02x\n", + tvp5150_read(sd, TVP5150_SATURATION_CTL)); + printk("tvp5150: Hue control = 0x%02x\n", + tvp5150_read(sd, TVP5150_HUE_CTL)); + printk("tvp5150: Contrast control = 0x%02x\n", + tvp5150_read(sd, TVP5150_CONTRAST_CTL)); + printk("tvp5150: Outputs and data rates select = 0x%02x\n", + tvp5150_read(sd, TVP5150_DATA_RATE_SEL)); + printk("tvp5150: Configuration shared pins = 0x%02x\n", + tvp5150_read(sd, TVP5150_CONF_SHARED_PIN)); + printk("tvp5150: Active video cropping start = 0x%02x%02x\n", + tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_MSB), + tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_LSB)); + printk("tvp5150: Active video cropping stop = 0x%02x%02x\n", + tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_MSB), + tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_LSB)); + printk("tvp5150: Genlock/RTC = 0x%02x\n", + tvp5150_read(sd, TVP5150_GENLOCK)); + printk("tvp5150: Horizontal sync start = 0x%02x\n", + tvp5150_read(sd, TVP5150_HORIZ_SYNC_START)); + printk("tvp5150: Vertical blanking start = 0x%02x\n", + tvp5150_read(sd, TVP5150_VERT_BLANKING_START)); + printk("tvp5150: Vertical blanking stop = 0x%02x\n", + tvp5150_read(sd, TVP5150_VERT_BLANKING_STOP)); + printk("tvp5150: Chrominance processing control #1 and #2 = %02x %02x\n", + tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_1), + tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_2)); + printk("tvp5150: Interrupt reset register B = 0x%02x\n", + tvp5150_read(sd, TVP5150_INT_RESET_REG_B)); + printk("tvp5150: Interrupt enable register B = 0x%02x\n", + tvp5150_read(sd, TVP5150_INT_ENABLE_REG_B)); + printk("tvp5150: Interrupt configuration register B = 0x%02x\n", + tvp5150_read(sd, TVP5150_INTT_CONFIG_REG_B)); + printk("tvp5150: Video standard = 0x%02x\n", + tvp5150_read(sd, TVP5150_VIDEO_STD)); + printk("tvp5150: Chroma gain factor: Cb=0x%02x Cr=0x%02x\n", + tvp5150_read(sd, TVP5150_CB_GAIN_FACT), + tvp5150_read(sd, TVP5150_CR_GAIN_FACTOR)); + printk("tvp5150: Macrovision on counter = 0x%02x\n", + tvp5150_read(sd, TVP5150_MACROVISION_ON_CTR)); + printk("tvp5150: Macrovision off counter = 0x%02x\n", + tvp5150_read(sd, TVP5150_MACROVISION_OFF_CTR)); + printk("tvp5150: ITU-R BT.656.%d timing(TVP5150AM1 only)\n", + (tvp5150_read(sd, TVP5150_REV_SELECT) & 1) ? 3 : 4); + printk("tvp5150: Device ID = %02x%02x\n", + tvp5150_read(sd, TVP5150_MSB_DEV_ID), + tvp5150_read(sd, TVP5150_LSB_DEV_ID)); + printk("tvp5150: ROM version = (hex) %02x.%02x\n", + tvp5150_read(sd, TVP5150_ROM_MAJOR_VER), + tvp5150_read(sd, TVP5150_ROM_MINOR_VER)); + printk("tvp5150: Vertical line count = 0x%02x%02x\n", + tvp5150_read(sd, TVP5150_VERT_LN_COUNT_MSB), + tvp5150_read(sd, TVP5150_VERT_LN_COUNT_LSB)); + printk("tvp5150: Interrupt status register B = 0x%02x\n", + tvp5150_read(sd, TVP5150_INT_STATUS_REG_B)); + printk("tvp5150: Interrupt active register B = 0x%02x\n", + tvp5150_read(sd, TVP5150_INT_ACTIVE_REG_B)); + printk("tvp5150: Status regs #1 to #5 = %02x %02x %02x %02x %02x\n", + tvp5150_read(sd, TVP5150_STATUS_REG_1), + tvp5150_read(sd, TVP5150_STATUS_REG_2), + tvp5150_read(sd, TVP5150_STATUS_REG_3), + tvp5150_read(sd, TVP5150_STATUS_REG_4), + tvp5150_read(sd, TVP5150_STATUS_REG_5)); + + dump_reg_range(sd, "Teletext filter 1", TVP5150_TELETEXT_FIL1_INI, + TVP5150_TELETEXT_FIL1_END, 8); + dump_reg_range(sd, "Teletext filter 2", TVP5150_TELETEXT_FIL2_INI, + TVP5150_TELETEXT_FIL2_END, 8); + + printk("tvp5150: Teletext filter enable = 0x%02x\n", + tvp5150_read(sd, TVP5150_TELETEXT_FIL_ENA)); + printk("tvp5150: Interrupt status register A = 0x%02x\n", + tvp5150_read(sd, TVP5150_INT_STATUS_REG_A)); + printk("tvp5150: Interrupt enable register A = 0x%02x\n", + tvp5150_read(sd, TVP5150_INT_ENABLE_REG_A)); + printk("tvp5150: Interrupt configuration = 0x%02x\n", + tvp5150_read(sd, TVP5150_INT_CONF)); + printk("tvp5150: VDP status register = 0x%02x\n", + tvp5150_read(sd, TVP5150_VDP_STATUS_REG)); + printk("tvp5150: FIFO word count = 0x%02x\n", + tvp5150_read(sd, TVP5150_FIFO_WORD_COUNT)); + printk("tvp5150: FIFO interrupt threshold = 0x%02x\n", + tvp5150_read(sd, TVP5150_FIFO_INT_THRESHOLD)); + printk("tvp5150: FIFO reset = 0x%02x\n", + tvp5150_read(sd, TVP5150_FIFO_RESET)); + printk("tvp5150: Line number interrupt = 0x%02x\n", + tvp5150_read(sd, TVP5150_LINE_NUMBER_INT)); + printk("tvp5150: Pixel alignment register = 0x%02x%02x\n", + tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_HIGH), + tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_LOW)); + printk("tvp5150: FIFO output control = 0x%02x\n", + tvp5150_read(sd, TVP5150_FIFO_OUT_CTRL)); + printk("tvp5150: Full field enable = 0x%02x\n", + tvp5150_read(sd, TVP5150_FULL_FIELD_ENA)); + printk("tvp5150: Full field mode register = 0x%02x\n", + tvp5150_read(sd, TVP5150_FULL_FIELD_MODE_REG)); + + dump_reg_range(sd, "CC data", TVP5150_CC_DATA_INI, + TVP5150_CC_DATA_END, 8); + + dump_reg_range(sd, "WSS data", TVP5150_WSS_DATA_INI, + TVP5150_WSS_DATA_END, 8); + + dump_reg_range(sd, "VPS data", TVP5150_VPS_DATA_INI, + TVP5150_VPS_DATA_END, 8); + + dump_reg_range(sd, "VITC data", TVP5150_VITC_DATA_INI, + TVP5150_VITC_DATA_END, 10); + + dump_reg_range(sd, "Line mode", TVP5150_LINE_MODE_INI, + TVP5150_LINE_MODE_END, 8); + return 0; +} + +/**************************************************************************** + Basic functions + ****************************************************************************/ + +static inline void tvp5150_selmux(struct v4l2_subdev *sd) +{ + int opmode = 0; + struct tvp5150 *decoder = to_tvp5150(sd); + int input = 0; + int val; + + if ((decoder->output & TVP5150_BLACK_SCREEN) || !decoder->enable) + input = 8; + + switch (decoder->input) { + case TVP5150_COMPOSITE1: + input |= 2; + /* fall through */ + case TVP5150_COMPOSITE0: + break; + case TVP5150_SVIDEO: + default: + input |= 1; + break; + } + + v4l2_dbg(1, debug, sd, "Selecting video route: route input=%i, output=%i " + "=> tvp5150 input=%i, opmode=%i\n", + decoder->input, decoder->output, + input, opmode); + + tvp5150_write(sd, TVP5150_OP_MODE_CTL, opmode); + tvp5150_write(sd, TVP5150_VD_IN_SRC_SEL_1, input); + + /* Svideo should enable YCrCb output and disable GPCL output + * For Composite and TV, it should be the reverse + */ + val = tvp5150_read(sd, TVP5150_MISC_CTL); + if (val < 0) { + v4l2_err(sd, "%s: failed with error = %d\n", __func__, val); + return; + } + + if (decoder->input == TVP5150_SVIDEO) + val = (val & ~0x40) | 0x10; + else + val = (val & ~0x10) | 0x40; + tvp5150_write(sd, TVP5150_MISC_CTL, val); +}; + +struct i2c_reg_value { + unsigned char reg; + unsigned char value; +}; + +/* Default values as sugested at TVP5150AM1 datasheet */ +static const struct i2c_reg_value tvp5150_init_default[] = { + { /* 0x00 */ + TVP5150_VD_IN_SRC_SEL_1,0x00 + }, + { /* 0x01 */ + TVP5150_ANAL_CHL_CTL,0x15 + }, + { /* 0x02 */ + TVP5150_OP_MODE_CTL,0x00 + }, + { /* 0x03 */ + TVP5150_MISC_CTL,0x01 + }, + { /* 0x06 */ + TVP5150_COLOR_KIL_THSH_CTL,0x10 + }, + { /* 0x07 */ + TVP5150_LUMA_PROC_CTL_1,0x60 + }, + { /* 0x08 */ + TVP5150_LUMA_PROC_CTL_2,0x00 + }, + { /* 0x09 */ + TVP5150_BRIGHT_CTL,0x80 + }, + { /* 0x0a */ + TVP5150_SATURATION_CTL,0x80 + }, + { /* 0x0b */ + TVP5150_HUE_CTL,0x00 + }, + { /* 0x0c */ + TVP5150_CONTRAST_CTL,0x80 + }, + { /* 0x0d */ + TVP5150_DATA_RATE_SEL,0x47 + }, + { /* 0x0e */ + TVP5150_LUMA_PROC_CTL_3,0x00 + }, + { /* 0x0f */ + TVP5150_CONF_SHARED_PIN,0x08 + }, + { /* 0x11 */ + TVP5150_ACT_VD_CROP_ST_MSB,0x00 + }, + { /* 0x12 */ + TVP5150_ACT_VD_CROP_ST_LSB,0x00 + }, + { /* 0x13 */ + TVP5150_ACT_VD_CROP_STP_MSB,0x00 + }, + { /* 0x14 */ + TVP5150_ACT_VD_CROP_STP_LSB,0x00 + }, + { /* 0x15 */ + TVP5150_GENLOCK,0x01 + }, + { /* 0x16 */ + TVP5150_HORIZ_SYNC_START,0x80 + }, + { /* 0x18 */ + TVP5150_VERT_BLANKING_START,0x00 + }, + { /* 0x19 */ + TVP5150_VERT_BLANKING_STOP,0x00 + }, + { /* 0x1a */ + TVP5150_CHROMA_PROC_CTL_1,0x0c + }, + { /* 0x1b */ + TVP5150_CHROMA_PROC_CTL_2,0x14 + }, + { /* 0x1c */ + TVP5150_INT_RESET_REG_B,0x00 + }, + { /* 0x1d */ + TVP5150_INT_ENABLE_REG_B,0x00 + }, + { /* 0x1e */ + TVP5150_INTT_CONFIG_REG_B,0x00 + }, + { /* 0x28 */ + TVP5150_VIDEO_STD,0x00 + }, + { /* 0x2e */ + TVP5150_MACROVISION_ON_CTR,0x0f + }, + { /* 0x2f */ + TVP5150_MACROVISION_OFF_CTR,0x01 + }, + { /* 0xbb */ + TVP5150_TELETEXT_FIL_ENA,0x00 + }, + { /* 0xc0 */ + TVP5150_INT_STATUS_REG_A,0x00 + }, + { /* 0xc1 */ + TVP5150_INT_ENABLE_REG_A,0x00 + }, + { /* 0xc2 */ + TVP5150_INT_CONF,0x04 + }, + { /* 0xc8 */ + TVP5150_FIFO_INT_THRESHOLD,0x80 + }, + { /* 0xc9 */ + TVP5150_FIFO_RESET,0x00 + }, + { /* 0xca */ + TVP5150_LINE_NUMBER_INT,0x00 + }, + { /* 0xcb */ + TVP5150_PIX_ALIGN_REG_LOW,0x4e + }, + { /* 0xcc */ + TVP5150_PIX_ALIGN_REG_HIGH,0x00 + }, + { /* 0xcd */ + TVP5150_FIFO_OUT_CTRL,0x01 + }, + { /* 0xcf */ + TVP5150_FULL_FIELD_ENA,0x00 + }, + { /* 0xd0 */ + TVP5150_LINE_MODE_INI,0x00 + }, + { /* 0xfc */ + TVP5150_FULL_FIELD_MODE_REG,0x7f + }, + { /* end of data */ + 0xff,0xff + } +}; + +/* Default values as sugested at TVP5150AM1 datasheet */ +static const struct i2c_reg_value tvp5150_init_enable[] = { + { + TVP5150_CONF_SHARED_PIN, 2 + },{ /* Automatic offset and AGC enabled */ + TVP5150_ANAL_CHL_CTL, 0x15 + },{ /* Activate YCrCb output 0x9 or 0xd ? */ + TVP5150_MISC_CTL, 0x6f + },{ /* Activates video std autodetection for all standards */ + TVP5150_AUTOSW_MSK, 0x0 + },{ /* Default format: 0x47. For 4:2:2: 0x40 */ + TVP5150_DATA_RATE_SEL, 0x47 + },{ + TVP5150_CHROMA_PROC_CTL_1, 0x0c + },{ + TVP5150_CHROMA_PROC_CTL_2, 0x54 + },{ /* Non documented, but initialized on WinTV USB2 */ + 0x27, 0x20 + },{ + 0xff,0xff + } +}; + +struct tvp5150_vbi_type { + unsigned int vbi_type; + unsigned int ini_line; + unsigned int end_line; + unsigned int by_field :1; +}; + +struct i2c_vbi_ram_value { + u16 reg; + struct tvp5150_vbi_type type; + unsigned char values[16]; +}; + +/* This struct have the values for each supported VBI Standard + * by + tvp5150_vbi_types should follow the same order as vbi_ram_default + * value 0 means rom position 0x10, value 1 means rom position 0x30 + * and so on. There are 16 possible locations from 0 to 15. + */ + +static struct i2c_vbi_ram_value vbi_ram_default[] = +{ + /* FIXME: Current api doesn't handle all VBI types, those not + yet supported are placed under #if 0 */ +#if 0 + {0x010, /* Teletext, SECAM, WST System A */ + {V4L2_SLICED_TELETEXT_SECAM,6,23,1}, + { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x26, + 0xe6, 0xb4, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00 } + }, +#endif + {0x030, /* Teletext, PAL, WST System B */ + {V4L2_SLICED_TELETEXT_B,6,22,1}, + { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x2b, + 0xa6, 0x72, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00 } + }, +#if 0 + {0x050, /* Teletext, PAL, WST System C */ + {V4L2_SLICED_TELETEXT_PAL_C,6,22,1}, + { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22, + 0xa6, 0x98, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } + }, + {0x070, /* Teletext, NTSC, WST System B */ + {V4L2_SLICED_TELETEXT_NTSC_B,10,21,1}, + { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x23, + 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } + }, + {0x090, /* Tetetext, NTSC NABTS System C */ + {V4L2_SLICED_TELETEXT_NTSC_C,10,21,1}, + { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22, + 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x15, 0x00 } + }, + {0x0b0, /* Teletext, NTSC-J, NABTS System D */ + {V4L2_SLICED_TELETEXT_NTSC_D,10,21,1}, + { 0xaa, 0xaa, 0xff, 0xff, 0xa7, 0x2e, 0x20, 0x23, + 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } + }, + {0x0d0, /* Closed Caption, PAL/SECAM */ + {V4L2_SLICED_CAPTION_625,22,22,1}, + { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02, + 0xa6, 0x7b, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 } + }, +#endif + {0x0f0, /* Closed Caption, NTSC */ + {V4L2_SLICED_CAPTION_525,21,21,1}, + { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02, + 0x69, 0x8c, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 } + }, + {0x110, /* Wide Screen Signal, PAL/SECAM */ + {V4L2_SLICED_WSS_625,23,23,1}, + { 0x5b, 0x55, 0xc5, 0xff, 0x00, 0x71, 0x6e, 0x42, + 0xa6, 0xcd, 0x0f, 0x00, 0x00, 0x00, 0x3a, 0x00 } + }, +#if 0 + {0x130, /* Wide Screen Signal, NTSC C */ + {V4L2_SLICED_WSS_525,20,20,1}, + { 0x38, 0x00, 0x3f, 0x00, 0x00, 0x71, 0x6e, 0x43, + 0x69, 0x7c, 0x08, 0x00, 0x00, 0x00, 0x39, 0x00 } + }, + {0x150, /* Vertical Interval Timecode (VITC), PAL/SECAM */ + {V4l2_SLICED_VITC_625,6,22,0}, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49, + 0xa6, 0x85, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 } + }, + {0x170, /* Vertical Interval Timecode (VITC), NTSC */ + {V4l2_SLICED_VITC_525,10,20,0}, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49, + 0x69, 0x94, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 } + }, +#endif + {0x190, /* Video Program System (VPS), PAL */ + {V4L2_SLICED_VPS,16,16,0}, + { 0xaa, 0xaa, 0xff, 0xff, 0xba, 0xce, 0x2b, 0x0d, + 0xa6, 0xda, 0x0b, 0x00, 0x00, 0x00, 0x60, 0x00 } + }, + /* 0x1d0 User programmable */ + + /* End of struct */ + { (u16)-1 } +}; + +static int tvp5150_write_inittab(struct v4l2_subdev *sd, + const struct i2c_reg_value *regs) +{ + while (regs->reg != 0xff) { + tvp5150_write(sd, regs->reg, regs->value); + regs++; + } + return 0; +} + +static int tvp5150_vdp_init(struct v4l2_subdev *sd, + const struct i2c_vbi_ram_value *regs) +{ + unsigned int i; + + /* Disable Full Field */ + tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0); + + /* Before programming, Line mode should be at 0xff */ + for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++) + tvp5150_write(sd, i, 0xff); + + /* Load Ram Table */ + while (regs->reg != (u16)-1) { + tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_HIGH, regs->reg >> 8); + tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_LOW, regs->reg); + + for (i = 0; i < 16; i++) + tvp5150_write(sd, TVP5150_VDP_CONF_RAM_DATA, regs->values[i]); + + regs++; + } + return 0; +} + +/* Fills VBI capabilities based on i2c_vbi_ram_value struct */ +static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd, + struct v4l2_sliced_vbi_cap *cap) +{ + const struct i2c_vbi_ram_value *regs = vbi_ram_default; + int line; + + v4l2_dbg(1, debug, sd, "g_sliced_vbi_cap\n"); + memset(cap, 0, sizeof *cap); + + while (regs->reg != (u16)-1 ) { + for (line=regs->type.ini_line;line<=regs->type.end_line;line++) { + cap->service_lines[0][line] |= regs->type.vbi_type; + } + cap->service_set |= regs->type.vbi_type; + + regs++; + } + return 0; +} + +/* Set vbi processing + * type - one of tvp5150_vbi_types + * line - line to gather data + * fields: bit 0 field1, bit 1, field2 + * flags (default=0xf0) is a bitmask, were set means: + * bit 7: enable filtering null bytes on CC + * bit 6: send data also to FIFO + * bit 5: don't allow data with errors on FIFO + * bit 4: enable ECC when possible + * pix_align = pix alignment: + * LSB = field1 + * MSB = field2 + */ +static int tvp5150_set_vbi(struct v4l2_subdev *sd, + const struct i2c_vbi_ram_value *regs, + unsigned int type,u8 flags, int line, + const int fields) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + v4l2_std_id std = decoder->norm; + u8 reg; + int pos=0; + + if (std == V4L2_STD_ALL) { + v4l2_err(sd, "VBI can't be configured without knowing number of lines\n"); + return 0; + } else if (std & V4L2_STD_625_50) { + /* Don't follow NTSC Line number convension */ + line += 3; + } + + if (line<6||line>27) + return 0; + + while (regs->reg != (u16)-1 ) { + if ((type & regs->type.vbi_type) && + (line>=regs->type.ini_line) && + (line<=regs->type.end_line)) { + type=regs->type.vbi_type; + break; + } + + regs++; + pos++; + } + if (regs->reg == (u16)-1) + return 0; + + type=pos | (flags & 0xf0); + reg=((line-6)<<1)+TVP5150_LINE_MODE_INI; + + if (fields&1) { + tvp5150_write(sd, reg, type); + } + + if (fields&2) { + tvp5150_write(sd, reg+1, type); + } + + return type; +} + +static int tvp5150_get_vbi(struct v4l2_subdev *sd, + const struct i2c_vbi_ram_value *regs, int line) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + v4l2_std_id std = decoder->norm; + u8 reg; + int pos, type = 0; + int i, ret = 0; + + if (std == V4L2_STD_ALL) { + v4l2_err(sd, "VBI can't be configured without knowing number of lines\n"); + return 0; + } else if (std & V4L2_STD_625_50) { + /* Don't follow NTSC Line number convension */ + line += 3; + } + + if (line < 6 || line > 27) + return 0; + + reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI; + + for (i = 0; i <= 1; i++) { + ret = tvp5150_read(sd, reg + i); + if (ret < 0) { + v4l2_err(sd, "%s: failed with error = %d\n", + __func__, ret); + return 0; + } + pos = ret & 0x0f; + if (pos < 0x0f) + type |= regs[pos].type.vbi_type; + } + + return type; +} + +static int tvp5150_set_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + int fmt = 0; + + decoder->norm = std; + + /* First tests should be against specific std */ + + if (std == V4L2_STD_ALL) { + fmt = VIDEO_STD_AUTO_SWITCH_BIT; /* Autodetect mode */ + } else if (std & V4L2_STD_NTSC_443) { + fmt = VIDEO_STD_NTSC_4_43_BIT; + } else if (std & V4L2_STD_PAL_M) { + fmt = VIDEO_STD_PAL_M_BIT; + } else if (std & (V4L2_STD_PAL_N | V4L2_STD_PAL_Nc)) { + fmt = VIDEO_STD_PAL_COMBINATION_N_BIT; + } else { + /* Then, test against generic ones */ + if (std & V4L2_STD_NTSC) + fmt = VIDEO_STD_NTSC_MJ_BIT; + else if (std & V4L2_STD_PAL) + fmt = VIDEO_STD_PAL_BDGHIN_BIT; + else if (std & V4L2_STD_SECAM) + fmt = VIDEO_STD_SECAM_BIT; + } + + v4l2_dbg(1, debug, sd, "Set video std register to %d.\n", fmt); + tvp5150_write(sd, TVP5150_VIDEO_STD, fmt); + return 0; +} + +static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + + if (decoder->norm == std) + return 0; + + /* Change cropping height limits */ + if (std & V4L2_STD_525_60) + decoder->rect.height = TVP5150_V_MAX_525_60; + else + decoder->rect.height = TVP5150_V_MAX_OTHERS; + + + return tvp5150_set_std(sd, std); +} + +static int tvp5150_reset(struct v4l2_subdev *sd, u32 val) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + + /* Initializes TVP5150 to its default values */ + tvp5150_write_inittab(sd, tvp5150_init_default); + + /* Initializes VDP registers */ + tvp5150_vdp_init(sd, vbi_ram_default); + + /* Selects decoder input */ + tvp5150_selmux(sd); + + /* Initializes TVP5150 to stream enabled values */ + tvp5150_write_inittab(sd, tvp5150_init_enable); + + /* Initialize image preferences */ + v4l2_ctrl_handler_setup(&decoder->hdl); + + tvp5150_set_std(sd, decoder->norm); + return 0; +}; + +static int tvp5150_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + tvp5150_write(sd, TVP5150_BRIGHT_CTL, ctrl->val); + return 0; + case V4L2_CID_CONTRAST: + tvp5150_write(sd, TVP5150_CONTRAST_CTL, ctrl->val); + return 0; + case V4L2_CID_SATURATION: + tvp5150_write(sd, TVP5150_SATURATION_CTL, ctrl->val); + return 0; + case V4L2_CID_HUE: + tvp5150_write(sd, TVP5150_HUE_CTL, ctrl->val); + return 0; + } + return -EINVAL; +} + +static v4l2_std_id tvp5150_read_std(struct v4l2_subdev *sd) +{ + int val = tvp5150_read(sd, TVP5150_STATUS_REG_5); + + switch (val & 0x0F) { + case 0x01: + return V4L2_STD_NTSC; + case 0x03: + return V4L2_STD_PAL; + case 0x05: + return V4L2_STD_PAL_M; + case 0x07: + return V4L2_STD_PAL_N | V4L2_STD_PAL_Nc; + case 0x09: + return V4L2_STD_NTSC_443; + case 0xb: + return V4L2_STD_SECAM; + default: + return V4L2_STD_UNKNOWN; + } +} + +static int tvp5150_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, + enum v4l2_mbus_pixelcode *code) +{ + if (index) + return -EINVAL; + + *code = V4L2_MBUS_FMT_UYVY8_2X8; + return 0; +} + +static int tvp5150_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *f) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + + if (f == NULL) + return -EINVAL; + + tvp5150_reset(sd, 0); + + f->width = decoder->rect.width; + f->height = decoder->rect.height; + + f->code = V4L2_MBUS_FMT_UYVY8_2X8; + f->field = V4L2_FIELD_SEQ_TB; + f->colorspace = V4L2_COLORSPACE_SMPTE170M; + + v4l2_dbg(1, debug, sd, "width = %d, height = %d\n", f->width, + f->height); + return 0; +} + +static int tvp5150_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + struct v4l2_rect rect = a->c; + struct tvp5150 *decoder = to_tvp5150(sd); + v4l2_std_id std; + int hmax; + + v4l2_dbg(1, debug, sd, "%s left=%d, top=%d, width=%d, height=%d\n", + __func__, rect.left, rect.top, rect.width, rect.height); + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + /* tvp5150 has some special limits */ + rect.left = clamp(rect.left, 0, TVP5150_MAX_CROP_LEFT); + rect.width = clamp(rect.width, + TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left, + TVP5150_H_MAX - rect.left); + rect.top = clamp(rect.top, 0, TVP5150_MAX_CROP_TOP); + + /* Calculate height based on current standard */ + if (decoder->norm == V4L2_STD_ALL) + std = tvp5150_read_std(sd); + else + std = decoder->norm; + + if (std & V4L2_STD_525_60) + hmax = TVP5150_V_MAX_525_60; + else + hmax = TVP5150_V_MAX_OTHERS; + + rect.height = clamp(rect.height, + hmax - TVP5150_MAX_CROP_TOP - rect.top, + hmax - rect.top); + + tvp5150_write(sd, TVP5150_VERT_BLANKING_START, rect.top); + tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, + rect.top + rect.height - hmax); + tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_MSB, + rect.left >> TVP5150_CROP_SHIFT); + tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_LSB, + rect.left | (1 << TVP5150_CROP_SHIFT)); + tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_MSB, + (rect.left + rect.width - TVP5150_MAX_CROP_LEFT) >> + TVP5150_CROP_SHIFT); + tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_LSB, + rect.left + rect.width - TVP5150_MAX_CROP_LEFT); + + decoder->rect = rect; + + return 0; +} + +static int tvp5150_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd); + + a->c = decoder->rect; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + return 0; +} + +static int tvp5150_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd); + v4l2_std_id std; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + a->bounds.left = 0; + a->bounds.top = 0; + a->bounds.width = TVP5150_H_MAX; + + /* Calculate height based on current standard */ + if (decoder->norm == V4L2_STD_ALL) + std = tvp5150_read_std(sd); + else + std = decoder->norm; + + if (std & V4L2_STD_525_60) + a->bounds.height = TVP5150_V_MAX_525_60; + else + a->bounds.height = TVP5150_V_MAX_OTHERS; + + a->defrect = a->bounds; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + +/**************************************************************************** + I2C Command + ****************************************************************************/ + +static int tvp5150_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + + decoder->input = input; + decoder->output = output; + tvp5150_selmux(sd); + return 0; +} + +static int tvp5150_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) +{ + /* this is for capturing 36 raw vbi lines + if there's a way to cut off the beginning 2 vbi lines + with the tvp5150 then the vbi line count could be lowered + to 17 lines/field again, although I couldn't find a register + which could do that cropping */ + if (fmt->sample_format == V4L2_PIX_FMT_GREY) + tvp5150_write(sd, TVP5150_LUMA_PROC_CTL_1, 0x70); + if (fmt->count[0] == 18 && fmt->count[1] == 18) { + tvp5150_write(sd, TVP5150_VERT_BLANKING_START, 0x00); + tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, 0x01); + } + return 0; +} + +static int tvp5150_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) +{ + int i; + + if (svbi->service_set != 0) { + for (i = 0; i <= 23; i++) { + svbi->service_lines[1][i] = 0; + svbi->service_lines[0][i] = + tvp5150_set_vbi(sd, vbi_ram_default, + svbi->service_lines[0][i], 0xf0, i, 3); + } + /* Enables FIFO */ + tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 1); + } else { + /* Disables FIFO*/ + tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 0); + + /* Disable Full Field */ + tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0); + + /* Disable Line modes */ + for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++) + tvp5150_write(sd, i, 0xff); + } + return 0; +} + +static int tvp5150_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) +{ + int i, mask = 0; + + memset(svbi, 0, sizeof(*svbi)); + + for (i = 0; i <= 23; i++) { + svbi->service_lines[0][i] = + tvp5150_get_vbi(sd, vbi_ram_default, i); + mask |= svbi->service_lines[0][i]; + } + svbi->service_set = mask; + return 0; +} + +static int tvp5150_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + int rev; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + rev = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER) << 8 | + tvp5150_read(sd, TVP5150_ROM_MINOR_VER); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP5150, + rev); +} + + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + int res; + + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + res = tvp5150_read(sd, reg->reg & 0xff); + if (res < 0) { + v4l2_err(sd, "%s: failed with error = %d\n", __func__, res); + return res; + } + + reg->val = res; + reg->size = 1; + return 0; +} + +static int tvp5150_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + tvp5150_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif + +static int tvp5150_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + int status = tvp5150_read(sd, 0x88); + + vt->signal = ((status & 0x04) && (status & 0x02)) ? 0xffff : 0x0; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = { + .s_ctrl = tvp5150_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops tvp5150_core_ops = { + .log_status = tvp5150_log_status, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, + .s_std = tvp5150_s_std, + .reset = tvp5150_reset, + .g_chip_ident = tvp5150_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = tvp5150_g_register, + .s_register = tvp5150_s_register, +#endif +}; + +static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = { + .g_tuner = tvp5150_g_tuner, +}; + +static const struct v4l2_subdev_video_ops tvp5150_video_ops = { + .s_routing = tvp5150_s_routing, + .enum_mbus_fmt = tvp5150_enum_mbus_fmt, + .s_mbus_fmt = tvp5150_mbus_fmt, + .try_mbus_fmt = tvp5150_mbus_fmt, + .g_mbus_fmt = tvp5150_mbus_fmt, + .s_crop = tvp5150_s_crop, + .g_crop = tvp5150_g_crop, + .cropcap = tvp5150_cropcap, +}; + +static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = { + .g_sliced_vbi_cap = tvp5150_g_sliced_vbi_cap, + .g_sliced_fmt = tvp5150_g_sliced_fmt, + .s_sliced_fmt = tvp5150_s_sliced_fmt, + .s_raw_fmt = tvp5150_s_raw_fmt, +}; + +static const struct v4l2_subdev_ops tvp5150_ops = { + .core = &tvp5150_core_ops, + .tuner = &tvp5150_tuner_ops, + .video = &tvp5150_video_ops, + .vbi = &tvp5150_vbi_ops, +}; + + +/**************************************************************************** + I2C Client & Driver + ****************************************************************************/ + +static int tvp5150_probe(struct i2c_client *c, + const struct i2c_device_id *id) +{ + struct tvp5150 *core; + struct v4l2_subdev *sd; + int tvp5150_id[4]; + int i, res; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(c->adapter, + I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return -EIO; + + core = kzalloc(sizeof(struct tvp5150), GFP_KERNEL); + if (!core) { + return -ENOMEM; + } + sd = &core->sd; + v4l2_i2c_subdev_init(sd, c, &tvp5150_ops); + + /* + * Read consequent registers - TVP5150_MSB_DEV_ID, TVP5150_LSB_DEV_ID, + * TVP5150_ROM_MAJOR_VER, TVP5150_ROM_MINOR_VER + */ + for (i = 0; i < 4; i++) { + res = tvp5150_read(sd, TVP5150_MSB_DEV_ID + i); + if (res < 0) + goto free_core; + tvp5150_id[i] = res; + } + + v4l_info(c, "chip found @ 0x%02x (%s)\n", + c->addr << 1, c->adapter->name); + + if (tvp5150_id[2] == 4 && tvp5150_id[3] == 0) { /* Is TVP5150AM1 */ + v4l2_info(sd, "tvp%02x%02xam1 detected.\n", + tvp5150_id[0], tvp5150_id[1]); + + /* ITU-T BT.656.4 timing */ + tvp5150_write(sd, TVP5150_REV_SELECT, 0); + } else { + /* Is TVP5150A */ + if (tvp5150_id[2] == 3 || tvp5150_id[3] == 0x21) { + v4l2_info(sd, "tvp%02x%02xa detected.\n", + tvp5150_id[2], tvp5150_id[3]); + } else { + v4l2_info(sd, "*** unknown tvp%02x%02x chip detected.\n", + tvp5150_id[2], tvp5150_id[3]); + v4l2_info(sd, "*** Rom ver is %d.%d\n", + tvp5150_id[2], tvp5150_id[3]); + } + } + + core->norm = V4L2_STD_ALL; /* Default is autodetect */ + core->input = TVP5150_COMPOSITE1; + core->enable = 1; + + v4l2_ctrl_handler_init(&core->hdl, 4); + v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 128); + v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 128); + v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + sd->ctrl_handler = &core->hdl; + if (core->hdl.error) { + res = core->hdl.error; + v4l2_ctrl_handler_free(&core->hdl); + goto free_core; + } + v4l2_ctrl_handler_setup(&core->hdl); + + /* Default is no cropping */ + core->rect.top = 0; + if (tvp5150_read_std(sd) & V4L2_STD_525_60) + core->rect.height = TVP5150_V_MAX_525_60; + else + core->rect.height = TVP5150_V_MAX_OTHERS; + core->rect.left = 0; + core->rect.width = TVP5150_H_MAX; + + if (debug > 1) + tvp5150_log_status(sd); + return 0; + +free_core: + kfree(core); + return res; +} + +static int tvp5150_remove(struct i2c_client *c) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(c); + struct tvp5150 *decoder = to_tvp5150(sd); + + v4l2_dbg(1, debug, sd, + "tvp5150.c: removing tvp5150 adapter on address 0x%x\n", + c->addr << 1); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&decoder->hdl); + kfree(to_tvp5150(sd)); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id tvp5150_id[] = { + { "tvp5150", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tvp5150_id); + +static struct i2c_driver tvp5150_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tvp5150", + }, + .probe = tvp5150_probe, + .remove = tvp5150_remove, + .id_table = tvp5150_id, +}; + +module_i2c_driver(tvp5150_driver); diff --git a/drivers/media/i2c/tvp5150_reg.h b/drivers/media/i2c/tvp5150_reg.h new file mode 100644 index 00000000000..25a99494491 --- /dev/null +++ b/drivers/media/i2c/tvp5150_reg.h @@ -0,0 +1,139 @@ +/* + * tvp5150 - Texas Instruments TVP5150A/AM1 video decoder registers + * + * Copyright (c) 2005,2006 Mauro Carvalho Chehab (mchehab@infradead.org) + * This code is placed under the terms of the GNU General Public License v2 + */ + +#define TVP5150_VD_IN_SRC_SEL_1 0x00 /* Video input source selection #1 */ +#define TVP5150_ANAL_CHL_CTL 0x01 /* Analog channel controls */ +#define TVP5150_OP_MODE_CTL 0x02 /* Operation mode controls */ +#define TVP5150_MISC_CTL 0x03 /* Miscellaneous controls */ +#define TVP5150_AUTOSW_MSK 0x04 /* Autoswitch mask: TVP5150A / TVP5150AM */ + +/* Reserved 05h */ + +#define TVP5150_COLOR_KIL_THSH_CTL 0x06 /* Color killer threshold control */ +#define TVP5150_LUMA_PROC_CTL_1 0x07 /* Luminance processing control #1 */ +#define TVP5150_LUMA_PROC_CTL_2 0x08 /* Luminance processing control #2 */ +#define TVP5150_BRIGHT_CTL 0x09 /* Brightness control */ +#define TVP5150_SATURATION_CTL 0x0a /* Color saturation control */ +#define TVP5150_HUE_CTL 0x0b /* Hue control */ +#define TVP5150_CONTRAST_CTL 0x0c /* Contrast control */ +#define TVP5150_DATA_RATE_SEL 0x0d /* Outputs and data rates select */ +#define TVP5150_LUMA_PROC_CTL_3 0x0e /* Luminance processing control #3 */ +#define TVP5150_CONF_SHARED_PIN 0x0f /* Configuration shared pins */ + +/* Reserved 10h */ + +#define TVP5150_ACT_VD_CROP_ST_MSB 0x11 /* Active video cropping start MSB */ +#define TVP5150_ACT_VD_CROP_ST_LSB 0x12 /* Active video cropping start LSB */ +#define TVP5150_ACT_VD_CROP_STP_MSB 0x13 /* Active video cropping stop MSB */ +#define TVP5150_ACT_VD_CROP_STP_LSB 0x14 /* Active video cropping stop LSB */ +#define TVP5150_GENLOCK 0x15 /* Genlock/RTC */ +#define TVP5150_HORIZ_SYNC_START 0x16 /* Horizontal sync start */ + +/* Reserved 17h */ + +#define TVP5150_VERT_BLANKING_START 0x18 /* Vertical blanking start */ +#define TVP5150_VERT_BLANKING_STOP 0x19 /* Vertical blanking stop */ +#define TVP5150_CHROMA_PROC_CTL_1 0x1a /* Chrominance processing control #1 */ +#define TVP5150_CHROMA_PROC_CTL_2 0x1b /* Chrominance processing control #2 */ +#define TVP5150_INT_RESET_REG_B 0x1c /* Interrupt reset register B */ +#define TVP5150_INT_ENABLE_REG_B 0x1d /* Interrupt enable register B */ +#define TVP5150_INTT_CONFIG_REG_B 0x1e /* Interrupt configuration register B */ + +/* Reserved 1Fh-27h */ + +#define VIDEO_STD_MASK (0x07 >> 1) +#define TVP5150_VIDEO_STD 0x28 /* Video standard */ +#define VIDEO_STD_AUTO_SWITCH_BIT 0x00 +#define VIDEO_STD_NTSC_MJ_BIT 0x02 +#define VIDEO_STD_PAL_BDGHIN_BIT 0x04 +#define VIDEO_STD_PAL_M_BIT 0x06 +#define VIDEO_STD_PAL_COMBINATION_N_BIT 0x08 +#define VIDEO_STD_NTSC_4_43_BIT 0x0a +#define VIDEO_STD_SECAM_BIT 0x0c + +#define VIDEO_STD_NTSC_MJ_BIT_AS 0x01 +#define VIDEO_STD_PAL_BDGHIN_BIT_AS 0x03 +#define VIDEO_STD_PAL_M_BIT_AS 0x05 +#define VIDEO_STD_PAL_COMBINATION_N_BIT_AS 0x07 +#define VIDEO_STD_NTSC_4_43_BIT_AS 0x09 +#define VIDEO_STD_SECAM_BIT_AS 0x0b + +/* Reserved 29h-2bh */ + +#define TVP5150_CB_GAIN_FACT 0x2c /* Cb gain factor */ +#define TVP5150_CR_GAIN_FACTOR 0x2d /* Cr gain factor */ +#define TVP5150_MACROVISION_ON_CTR 0x2e /* Macrovision on counter */ +#define TVP5150_MACROVISION_OFF_CTR 0x2f /* Macrovision off counter */ +#define TVP5150_REV_SELECT 0x30 /* revision select (TVP5150AM1 only) */ + +/* Reserved 31h-7Fh */ + +#define TVP5150_MSB_DEV_ID 0x80 /* MSB of device ID */ +#define TVP5150_LSB_DEV_ID 0x81 /* LSB of device ID */ +#define TVP5150_ROM_MAJOR_VER 0x82 /* ROM major version */ +#define TVP5150_ROM_MINOR_VER 0x83 /* ROM minor version */ +#define TVP5150_VERT_LN_COUNT_MSB 0x84 /* Vertical line count MSB */ +#define TVP5150_VERT_LN_COUNT_LSB 0x85 /* Vertical line count LSB */ +#define TVP5150_INT_STATUS_REG_B 0x86 /* Interrupt status register B */ +#define TVP5150_INT_ACTIVE_REG_B 0x87 /* Interrupt active register B */ +#define TVP5150_STATUS_REG_1 0x88 /* Status register #1 */ +#define TVP5150_STATUS_REG_2 0x89 /* Status register #2 */ +#define TVP5150_STATUS_REG_3 0x8a /* Status register #3 */ +#define TVP5150_STATUS_REG_4 0x8b /* Status register #4 */ +#define TVP5150_STATUS_REG_5 0x8c /* Status register #5 */ +/* Reserved 8Dh-8Fh */ + /* Closed caption data registers */ +#define TVP5150_CC_DATA_INI 0x90 +#define TVP5150_CC_DATA_END 0x93 + + /* WSS data registers */ +#define TVP5150_WSS_DATA_INI 0x94 +#define TVP5150_WSS_DATA_END 0x99 + +/* VPS data registers */ +#define TVP5150_VPS_DATA_INI 0x9a +#define TVP5150_VPS_DATA_END 0xa6 + +/* VITC data registers */ +#define TVP5150_VITC_DATA_INI 0xa7 +#define TVP5150_VITC_DATA_END 0xaf + +#define TVP5150_VBI_FIFO_READ_DATA 0xb0 /* VBI FIFO read data */ + +/* Teletext filter 1 */ +#define TVP5150_TELETEXT_FIL1_INI 0xb1 +#define TVP5150_TELETEXT_FIL1_END 0xb5 + +/* Teletext filter 2 */ +#define TVP5150_TELETEXT_FIL2_INI 0xb6 +#define TVP5150_TELETEXT_FIL2_END 0xba + +#define TVP5150_TELETEXT_FIL_ENA 0xbb /* Teletext filter enable */ +/* Reserved BCh-BFh */ +#define TVP5150_INT_STATUS_REG_A 0xc0 /* Interrupt status register A */ +#define TVP5150_INT_ENABLE_REG_A 0xc1 /* Interrupt enable register A */ +#define TVP5150_INT_CONF 0xc2 /* Interrupt configuration */ +#define TVP5150_VDP_CONF_RAM_DATA 0xc3 /* VDP configuration RAM data */ +#define TVP5150_CONF_RAM_ADDR_LOW 0xc4 /* Configuration RAM address low byte */ +#define TVP5150_CONF_RAM_ADDR_HIGH 0xc5 /* Configuration RAM address high byte */ +#define TVP5150_VDP_STATUS_REG 0xc6 /* VDP status register */ +#define TVP5150_FIFO_WORD_COUNT 0xc7 /* FIFO word count */ +#define TVP5150_FIFO_INT_THRESHOLD 0xc8 /* FIFO interrupt threshold */ +#define TVP5150_FIFO_RESET 0xc9 /* FIFO reset */ +#define TVP5150_LINE_NUMBER_INT 0xca /* Line number interrupt */ +#define TVP5150_PIX_ALIGN_REG_LOW 0xcb /* Pixel alignment register low byte */ +#define TVP5150_PIX_ALIGN_REG_HIGH 0xcc /* Pixel alignment register high byte */ +#define TVP5150_FIFO_OUT_CTRL 0xcd /* FIFO output control */ +/* Reserved CEh */ +#define TVP5150_FULL_FIELD_ENA 0xcf /* Full field enable 1 */ + +/* Line mode registers */ +#define TVP5150_LINE_MODE_INI 0xd0 +#define TVP5150_LINE_MODE_END 0xfb + +#define TVP5150_FULL_FIELD_MODE_REG 0xfc /* Full field mode register */ +/* Reserved FDh-FFh */ diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c new file mode 100644 index 00000000000..fb6a5b57eb8 --- /dev/null +++ b/drivers/media/i2c/tvp7002.c @@ -0,0 +1,1145 @@ +/* Texas Instruments Triple 8-/10-BIT 165-/110-MSPS Video and Graphics + * Digitizer with Horizontal PLL registers + * + * Copyright (C) 2009 Texas Instruments Inc + * Author: Santiago Nunez-Corrales + * + * This code is partially based upon the TVP5150 driver + * written by Mauro Carvalho Chehab (mchehab@infradead.org), + * the TVP514x driver written by Vaibhav Hiremath + * and the TVP7002 driver in the TI LSP 2.10.00.14. Revisions by + * Muralidharan Karicheri and Snehaprabha Narnakaje (TI). + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tvp7002_reg.h" + +MODULE_DESCRIPTION("TI TVP7002 Video and Graphics Digitizer driver"); +MODULE_AUTHOR("Santiago Nunez-Corrales "); +MODULE_LICENSE("GPL"); + +/* Module Name */ +#define TVP7002_MODULE_NAME "tvp7002" + +/* I2C retry attempts */ +#define I2C_RETRY_COUNT (5) + +/* End of registers */ +#define TVP7002_EOR 0x5c + +/* Read write definition for registers */ +#define TVP7002_READ 0 +#define TVP7002_WRITE 1 +#define TVP7002_RESERVED 2 + +/* Interlaced vs progressive mask and shift */ +#define TVP7002_IP_SHIFT 5 +#define TVP7002_INPR_MASK (0x01 << TVP7002_IP_SHIFT) + +/* Shift for CPL and LPF registers */ +#define TVP7002_CL_SHIFT 8 +#define TVP7002_CL_MASK 0x0f + +/* Debug functions */ +static bool debug; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-2)"); + +/* Structure for register values */ +struct i2c_reg_value { + u8 reg; + u8 value; + u8 type; +}; + +/* + * Register default values (according to tvp7002 datasheet) + * In the case of read-only registers, the value (0xff) is + * never written. R/W functionality is controlled by the + * writable bit in the register struct definition. + */ +static const struct i2c_reg_value tvp7002_init_default[] = { + { TVP7002_CHIP_REV, 0xff, TVP7002_READ }, + { TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0x20, TVP7002_WRITE }, + { TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE }, + { TVP7002_HPLL_PHASE_SEL, 0x80, TVP7002_WRITE }, + { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, + { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, + { TVP7002_HSYNC_OUT_W, 0x60, TVP7002_WRITE }, + { TVP7002_B_FINE_GAIN, 0x00, TVP7002_WRITE }, + { TVP7002_G_FINE_GAIN, 0x00, TVP7002_WRITE }, + { TVP7002_R_FINE_GAIN, 0x00, TVP7002_WRITE }, + { TVP7002_B_FINE_OFF_MSBS, 0x80, TVP7002_WRITE }, + { TVP7002_G_FINE_OFF_MSBS, 0x80, TVP7002_WRITE }, + { TVP7002_R_FINE_OFF_MSBS, 0x80, TVP7002_WRITE }, + { TVP7002_SYNC_CTL_1, 0x20, TVP7002_WRITE }, + { TVP7002_HPLL_AND_CLAMP_CTL, 0x2e, TVP7002_WRITE }, + { TVP7002_SYNC_ON_G_THRS, 0x5d, TVP7002_WRITE }, + { TVP7002_SYNC_SEPARATOR_THRS, 0x47, TVP7002_WRITE }, + { TVP7002_HPLL_PRE_COAST, 0x00, TVP7002_WRITE }, + { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, + { TVP7002_SYNC_DETECT_STAT, 0xff, TVP7002_READ }, + { TVP7002_OUT_FORMATTER, 0x47, TVP7002_WRITE }, + { TVP7002_MISC_CTL_1, 0x01, TVP7002_WRITE }, + { TVP7002_MISC_CTL_2, 0x00, TVP7002_WRITE }, + { TVP7002_MISC_CTL_3, 0x01, TVP7002_WRITE }, + { TVP7002_IN_MUX_SEL_1, 0x00, TVP7002_WRITE }, + { TVP7002_IN_MUX_SEL_2, 0x67, TVP7002_WRITE }, + { TVP7002_B_AND_G_COARSE_GAIN, 0x77, TVP7002_WRITE }, + { TVP7002_R_COARSE_GAIN, 0x07, TVP7002_WRITE }, + { TVP7002_FINE_OFF_LSBS, 0x00, TVP7002_WRITE }, + { TVP7002_B_COARSE_OFF, 0x10, TVP7002_WRITE }, + { TVP7002_G_COARSE_OFF, 0x10, TVP7002_WRITE }, + { TVP7002_R_COARSE_OFF, 0x10, TVP7002_WRITE }, + { TVP7002_HSOUT_OUT_START, 0x08, TVP7002_WRITE }, + { TVP7002_MISC_CTL_4, 0x00, TVP7002_WRITE }, + { TVP7002_B_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ }, + { TVP7002_G_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ }, + { TVP7002_R_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ }, + { TVP7002_AUTO_LVL_CTL_ENABLE, 0x80, TVP7002_WRITE }, + { TVP7002_DGTL_ALC_OUT_MSBS, 0xff, TVP7002_READ }, + { TVP7002_AUTO_LVL_CTL_FILTER, 0x53, TVP7002_WRITE }, + { 0x29, 0x08, TVP7002_RESERVED }, + { TVP7002_FINE_CLAMP_CTL, 0x07, TVP7002_WRITE }, + /* PWR_CTL is controlled only by the probe and reset functions */ + { TVP7002_PWR_CTL, 0x00, TVP7002_RESERVED }, + { TVP7002_ADC_SETUP, 0x50, TVP7002_WRITE }, + { TVP7002_COARSE_CLAMP_CTL, 0x00, TVP7002_WRITE }, + { TVP7002_SOG_CLAMP, 0x80, TVP7002_WRITE }, + { TVP7002_RGB_COARSE_CLAMP_CTL, 0x8c, TVP7002_WRITE }, + { TVP7002_SOG_COARSE_CLAMP_CTL, 0x04, TVP7002_WRITE }, + { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, + { 0x32, 0x18, TVP7002_RESERVED }, + { 0x33, 0x60, TVP7002_RESERVED }, + { TVP7002_MVIS_STRIPPER_W, 0xff, TVP7002_RESERVED }, + { TVP7002_VSYNC_ALGN, 0x10, TVP7002_WRITE }, + { TVP7002_SYNC_BYPASS, 0x00, TVP7002_WRITE }, + { TVP7002_L_FRAME_STAT_LSBS, 0xff, TVP7002_READ }, + { TVP7002_L_FRAME_STAT_MSBS, 0xff, TVP7002_READ }, + { TVP7002_CLK_L_STAT_LSBS, 0xff, TVP7002_READ }, + { TVP7002_CLK_L_STAT_MSBS, 0xff, TVP7002_READ }, + { TVP7002_HSYNC_W, 0xff, TVP7002_READ }, + { TVP7002_VSYNC_W, 0xff, TVP7002_READ }, + { TVP7002_L_LENGTH_TOL, 0x03, TVP7002_WRITE }, + { 0x3e, 0x60, TVP7002_RESERVED }, + { TVP7002_VIDEO_BWTH_CTL, 0x01, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_LSBS, 0x01, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_MSBS, 0x2c, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_LSBS, 0x06, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_MSBS, 0x2c, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_DURATION, 0x1e, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, + { TVP7002_FBIT_F_0_START_L_OFF, 0x00, TVP7002_WRITE }, + { TVP7002_FBIT_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, + { TVP7002_YUV_Y_G_COEF_LSBS, 0xe3, TVP7002_WRITE }, + { TVP7002_YUV_Y_G_COEF_MSBS, 0x16, TVP7002_WRITE }, + { TVP7002_YUV_Y_B_COEF_LSBS, 0x4f, TVP7002_WRITE }, + { TVP7002_YUV_Y_B_COEF_MSBS, 0x02, TVP7002_WRITE }, + { TVP7002_YUV_Y_R_COEF_LSBS, 0xce, TVP7002_WRITE }, + { TVP7002_YUV_Y_R_COEF_MSBS, 0x06, TVP7002_WRITE }, + { TVP7002_YUV_U_G_COEF_LSBS, 0xab, TVP7002_WRITE }, + { TVP7002_YUV_U_G_COEF_MSBS, 0xf3, TVP7002_WRITE }, + { TVP7002_YUV_U_B_COEF_LSBS, 0x00, TVP7002_WRITE }, + { TVP7002_YUV_U_B_COEF_MSBS, 0x10, TVP7002_WRITE }, + { TVP7002_YUV_U_R_COEF_LSBS, 0x55, TVP7002_WRITE }, + { TVP7002_YUV_U_R_COEF_MSBS, 0xfc, TVP7002_WRITE }, + { TVP7002_YUV_V_G_COEF_LSBS, 0x78, TVP7002_WRITE }, + { TVP7002_YUV_V_G_COEF_MSBS, 0xf1, TVP7002_WRITE }, + { TVP7002_YUV_V_B_COEF_LSBS, 0x88, TVP7002_WRITE }, + { TVP7002_YUV_V_B_COEF_MSBS, 0xfe, TVP7002_WRITE }, + { TVP7002_YUV_V_R_COEF_LSBS, 0x00, TVP7002_WRITE }, + { TVP7002_YUV_V_R_COEF_MSBS, 0x10, TVP7002_WRITE }, + /* This signals end of register values */ + { TVP7002_EOR, 0xff, TVP7002_RESERVED } +}; + +/* Register parameters for 480P */ +static const struct i2c_reg_value tvp7002_parms_480P[] = { + { TVP7002_HPLL_FDBK_DIV_MSBS, 0x35, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0xa0, TVP7002_WRITE }, + { TVP7002_HPLL_CRTL, 0x02, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_LSBS, 0x91, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_MSBS, 0x00, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_LSBS, 0x0B, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_MSBS, 0x00, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_START_L_OFF, 0x03, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_START_L_OFF, 0x01, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_DURATION, 0x13, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_DURATION, 0x13, TVP7002_WRITE }, + { TVP7002_ALC_PLACEMENT, 0x18, TVP7002_WRITE }, + { TVP7002_CLAMP_START, 0x06, TVP7002_WRITE }, + { TVP7002_CLAMP_W, 0x10, TVP7002_WRITE }, + { TVP7002_HPLL_PRE_COAST, 0x03, TVP7002_WRITE }, + { TVP7002_HPLL_POST_COAST, 0x03, TVP7002_WRITE }, + { TVP7002_EOR, 0xff, TVP7002_RESERVED } +}; + +/* Register parameters for 576P */ +static const struct i2c_reg_value tvp7002_parms_576P[] = { + { TVP7002_HPLL_FDBK_DIV_MSBS, 0x36, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0x00, TVP7002_WRITE }, + { TVP7002_HPLL_CRTL, 0x18, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_LSBS, 0x9B, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_MSBS, 0x00, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_LSBS, 0x0F, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_MSBS, 0x00, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_START_L_OFF, 0x00, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, + { TVP7002_ALC_PLACEMENT, 0x18, TVP7002_WRITE }, + { TVP7002_CLAMP_START, 0x06, TVP7002_WRITE }, + { TVP7002_CLAMP_W, 0x10, TVP7002_WRITE }, + { TVP7002_HPLL_PRE_COAST, 0x03, TVP7002_WRITE }, + { TVP7002_HPLL_POST_COAST, 0x03, TVP7002_WRITE }, + { TVP7002_EOR, 0xff, TVP7002_RESERVED } +}; + +/* Register parameters for 1080I60 */ +static const struct i2c_reg_value tvp7002_parms_1080I60[] = { + { TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE }, + { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE }, + { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, + { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, + { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, + { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, + { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, + { TVP7002_EOR, 0xff, TVP7002_RESERVED } +}; + +/* Register parameters for 1080P60 */ +static const struct i2c_reg_value tvp7002_parms_1080P60[] = { + { TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE }, + { TVP7002_HPLL_CRTL, 0xE0, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE }, + { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, + { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, + { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, + { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, + { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, + { TVP7002_EOR, 0xff, TVP7002_RESERVED } +}; + +/* Register parameters for 1080I50 */ +static const struct i2c_reg_value tvp7002_parms_1080I50[] = { + { TVP7002_HPLL_FDBK_DIV_MSBS, 0xa5, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0x00, TVP7002_WRITE }, + { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE }, + { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, + { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, + { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, + { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, + { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, + { TVP7002_EOR, 0xff, TVP7002_RESERVED } +}; + +/* Register parameters for 720P60 */ +static const struct i2c_reg_value tvp7002_parms_720P60[] = { + { TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0x20, TVP7002_WRITE }, + { TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_LSBS, 0x4B, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_MSBS, 0x06, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, + { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, + { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, + { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, + { TVP7002_HPLL_PRE_COAST, 0x00, TVP7002_WRITE }, + { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, + { TVP7002_EOR, 0xff, TVP7002_RESERVED } +}; + +/* Register parameters for 720P50 */ +static const struct i2c_reg_value tvp7002_parms_720P50[] = { + { TVP7002_HPLL_FDBK_DIV_MSBS, 0x7b, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0xc0, TVP7002_WRITE }, + { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_LSBS, 0x4B, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_MSBS, 0x06, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, + { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, + { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, + { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, + { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, + { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, + { TVP7002_EOR, 0xff, TVP7002_RESERVED } +}; + +/* Preset definition for handling device operation */ +struct tvp7002_preset_definition { + u32 preset; + struct v4l2_dv_timings timings; + const struct i2c_reg_value *p_settings; + enum v4l2_colorspace color_space; + enum v4l2_field scanmode; + u16 progressive; + u16 lines_per_frame; + u16 cpl_min; + u16 cpl_max; +}; + +/* Struct list for digital video presets */ +static const struct tvp7002_preset_definition tvp7002_presets[] = { + { + V4L2_DV_720P60, + V4L2_DV_BT_CEA_1280X720P60, + tvp7002_parms_720P60, + V4L2_COLORSPACE_REC709, + V4L2_FIELD_NONE, + 1, + 0x2EE, + 135, + 153 + }, + { + V4L2_DV_1080I60, + V4L2_DV_BT_CEA_1920X1080I60, + tvp7002_parms_1080I60, + V4L2_COLORSPACE_REC709, + V4L2_FIELD_INTERLACED, + 0, + 0x465, + 181, + 205 + }, + { + V4L2_DV_1080I50, + V4L2_DV_BT_CEA_1920X1080I50, + tvp7002_parms_1080I50, + V4L2_COLORSPACE_REC709, + V4L2_FIELD_INTERLACED, + 0, + 0x465, + 217, + 245 + }, + { + V4L2_DV_720P50, + V4L2_DV_BT_CEA_1280X720P50, + tvp7002_parms_720P50, + V4L2_COLORSPACE_REC709, + V4L2_FIELD_NONE, + 1, + 0x2EE, + 163, + 183 + }, + { + V4L2_DV_1080P60, + V4L2_DV_BT_CEA_1920X1080P60, + tvp7002_parms_1080P60, + V4L2_COLORSPACE_REC709, + V4L2_FIELD_NONE, + 1, + 0x465, + 90, + 102 + }, + { + V4L2_DV_480P59_94, + V4L2_DV_BT_CEA_720X480P59_94, + tvp7002_parms_480P, + V4L2_COLORSPACE_SMPTE170M, + V4L2_FIELD_NONE, + 1, + 0x20D, + 0xffff, + 0xffff + }, + { + V4L2_DV_576P50, + V4L2_DV_BT_CEA_720X576P50, + tvp7002_parms_576P, + V4L2_COLORSPACE_SMPTE170M, + V4L2_FIELD_NONE, + 1, + 0x271, + 0xffff, + 0xffff + } +}; + +#define NUM_PRESETS ARRAY_SIZE(tvp7002_presets) + +/* Device definition */ +struct tvp7002 { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + const struct tvp7002_config *pdata; + + int ver; + int streaming; + + const struct tvp7002_preset_definition *current_preset; +}; + +/* + * to_tvp7002 - Obtain device handler TVP7002 + * @sd: ptr to v4l2_subdev struct + * + * Returns device handler tvp7002. + */ +static inline struct tvp7002 *to_tvp7002(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tvp7002, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct tvp7002, hdl)->sd; +} + +/* + * tvp7002_read - Read a value from a register in an TVP7002 + * @sd: ptr to v4l2_subdev struct + * @addr: TVP7002 register address + * @dst: pointer to 8-bit destination + * + * Returns value read if successful, or non-zero (-1) otherwise. + */ +static int tvp7002_read(struct v4l2_subdev *sd, u8 addr, u8 *dst) +{ + struct i2c_client *c = v4l2_get_subdevdata(sd); + int retry; + int error; + + for (retry = 0; retry < I2C_RETRY_COUNT; retry++) { + error = i2c_smbus_read_byte_data(c, addr); + + if (error >= 0) { + *dst = (u8)error; + return 0; + } + + msleep_interruptible(10); + } + v4l2_err(sd, "TVP7002 read error %d\n", error); + return error; +} + +/* + * tvp7002_read_err() - Read a register value with error code + * @sd: pointer to standard V4L2 sub-device structure + * @reg: destination register + * @val: value to be read + * @err: pointer to error value + * + * Read a value in a register and save error value in pointer. + * Also update the register table if successful + */ +static inline void tvp7002_read_err(struct v4l2_subdev *sd, u8 reg, + u8 *dst, int *err) +{ + if (!*err) + *err = tvp7002_read(sd, reg, dst); +} + +/* + * tvp7002_write() - Write a value to a register in TVP7002 + * @sd: ptr to v4l2_subdev struct + * @addr: TVP7002 register address + * @value: value to be written to the register + * + * Write a value to a register in an TVP7002 decoder device. + * Returns zero if successful, or non-zero otherwise. + */ +static int tvp7002_write(struct v4l2_subdev *sd, u8 addr, u8 value) +{ + struct i2c_client *c; + int retry; + int error; + + c = v4l2_get_subdevdata(sd); + + for (retry = 0; retry < I2C_RETRY_COUNT; retry++) { + error = i2c_smbus_write_byte_data(c, addr, value); + + if (error >= 0) + return 0; + + v4l2_warn(sd, "Write: retry ... %d\n", retry); + msleep_interruptible(10); + } + v4l2_err(sd, "TVP7002 write error %d\n", error); + return error; +} + +/* + * tvp7002_write_err() - Write a register value with error code + * @sd: pointer to standard V4L2 sub-device structure + * @reg: destination register + * @val: value to be written + * @err: pointer to error value + * + * Write a value in a register and save error value in pointer. + * Also update the register table if successful + */ +static inline void tvp7002_write_err(struct v4l2_subdev *sd, u8 reg, + u8 val, int *err) +{ + if (!*err) + *err = tvp7002_write(sd, reg, val); +} + +/* + * tvp7002_g_chip_ident() - Get chip identification number + * @sd: ptr to v4l2_subdev struct + * @chip: ptr to v4l2_dbg_chip_ident struct + * + * Obtains the chip's identification number. + * Returns zero or -EINVAL if read operation fails. + */ +static int tvp7002_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + u8 rev; + int error; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + error = tvp7002_read(sd, TVP7002_CHIP_REV, &rev); + + if (error < 0) + return error; + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP7002, rev); +} + +/* + * tvp7002_write_inittab() - Write initialization values + * @sd: ptr to v4l2_subdev struct + * @regs: ptr to i2c_reg_value struct + * + * Write initialization values. + * Returns zero or -EINVAL if read operation fails. + */ +static int tvp7002_write_inittab(struct v4l2_subdev *sd, + const struct i2c_reg_value *regs) +{ + int error = 0; + + /* Initialize the first (defined) registers */ + while (TVP7002_EOR != regs->reg) { + if (TVP7002_WRITE == regs->type) + tvp7002_write_err(sd, regs->reg, regs->value, &error); + regs++; + } + + return error; +} + +/* + * tvp7002_s_dv_preset() - Set digital video preset + * @sd: ptr to v4l2_subdev struct + * @dv_preset: ptr to v4l2_dv_preset struct + * + * Set the digital video preset for a TVP7002 decoder device. + * Returns zero when successful or -EINVAL if register access fails. + */ +static int tvp7002_s_dv_preset(struct v4l2_subdev *sd, + struct v4l2_dv_preset *dv_preset) +{ + struct tvp7002 *device = to_tvp7002(sd); + u32 preset; + int i; + + for (i = 0; i < NUM_PRESETS; i++) { + preset = tvp7002_presets[i].preset; + if (preset == dv_preset->preset) { + device->current_preset = &tvp7002_presets[i]; + return tvp7002_write_inittab(sd, tvp7002_presets[i].p_settings); + } + } + + return -EINVAL; +} + +static int tvp7002_s_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *dv_timings) +{ + struct tvp7002 *device = to_tvp7002(sd); + const struct v4l2_bt_timings *bt = &dv_timings->bt; + int i; + + if (dv_timings->type != V4L2_DV_BT_656_1120) + return -EINVAL; + for (i = 0; i < NUM_PRESETS; i++) { + const struct v4l2_bt_timings *t = &tvp7002_presets[i].timings.bt; + + if (!memcmp(bt, t, &bt->standards - &bt->width)) { + device->current_preset = &tvp7002_presets[i]; + return tvp7002_write_inittab(sd, tvp7002_presets[i].p_settings); + } + } + return -EINVAL; +} + +static int tvp7002_g_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *dv_timings) +{ + struct tvp7002 *device = to_tvp7002(sd); + + *dv_timings = device->current_preset->timings; + return 0; +} + +/* + * tvp7002_s_ctrl() - Set a control + * @ctrl: ptr to v4l2_ctrl struct + * + * Set a control in TVP7002 decoder device. + * Returns zero when successful or -EINVAL if register access fails. + */ +static int tvp7002_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + int error = 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + tvp7002_write_err(sd, TVP7002_R_FINE_GAIN, ctrl->val, &error); + tvp7002_write_err(sd, TVP7002_G_FINE_GAIN, ctrl->val, &error); + tvp7002_write_err(sd, TVP7002_B_FINE_GAIN, ctrl->val, &error); + return error; + } + return -EINVAL; +} + +/* + * tvp7002_mbus_fmt() - V4L2 decoder interface handler for try/s/g_mbus_fmt + * @sd: pointer to standard V4L2 sub-device structure + * @f: pointer to mediabus format structure + * + * Negotiate the image capture size and mediabus format. + * There is only one possible format, so this single function works for + * get, set and try. + */ +static int tvp7002_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) +{ + struct tvp7002 *device = to_tvp7002(sd); + struct v4l2_dv_enum_preset e_preset; + int error; + + /* Calculate height and width based on current standard */ + error = v4l_fill_dv_preset_info(device->current_preset->preset, &e_preset); + if (error) + return error; + + f->width = e_preset.width; + f->height = e_preset.height; + f->code = V4L2_MBUS_FMT_YUYV10_1X20; + f->field = device->current_preset->scanmode; + f->colorspace = device->current_preset->color_space; + + v4l2_dbg(1, debug, sd, "MBUS_FMT: Width - %d, Height - %d", + f->width, f->height); + return 0; +} + +/* + * tvp7002_query_dv_preset() - query DV preset + * @sd: pointer to standard V4L2 sub-device structure + * @qpreset: standard V4L2 v4l2_dv_preset structure + * + * Returns the current DV preset by TVP7002. If no active input is + * detected, returns -EINVAL + */ +static int tvp7002_query_dv(struct v4l2_subdev *sd, int *index) +{ + const struct tvp7002_preset_definition *presets = tvp7002_presets; + u8 progressive; + u32 lpfr; + u32 cpln; + int error = 0; + u8 lpf_lsb; + u8 lpf_msb; + u8 cpl_lsb; + u8 cpl_msb; + + /* Return invalid index if no active input is detected */ + *index = NUM_PRESETS; + + /* Read standards from device registers */ + tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_LSBS, &lpf_lsb, &error); + tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_MSBS, &lpf_msb, &error); + + if (error < 0) + return error; + + tvp7002_read_err(sd, TVP7002_CLK_L_STAT_LSBS, &cpl_lsb, &error); + tvp7002_read_err(sd, TVP7002_CLK_L_STAT_MSBS, &cpl_msb, &error); + + if (error < 0) + return error; + + /* Get lines per frame, clocks per line and interlaced/progresive */ + lpfr = lpf_lsb | ((TVP7002_CL_MASK & lpf_msb) << TVP7002_CL_SHIFT); + cpln = cpl_lsb | ((TVP7002_CL_MASK & cpl_msb) << TVP7002_CL_SHIFT); + progressive = (lpf_msb & TVP7002_INPR_MASK) >> TVP7002_IP_SHIFT; + + /* Do checking of video modes */ + for (*index = 0; *index < NUM_PRESETS; (*index)++, presets++) + if (lpfr == presets->lines_per_frame && + progressive == presets->progressive) { + if (presets->cpl_min == 0xffff) + break; + if (cpln >= presets->cpl_min && cpln <= presets->cpl_max) + break; + } + + if (*index == NUM_PRESETS) { + v4l2_dbg(1, debug, sd, "detection failed: lpf = %x, cpl = %x\n", + lpfr, cpln); + return -ENOLINK; + } + + /* Update lines per frame and clocks per line info */ + v4l2_dbg(1, debug, sd, "detected preset: %d\n", *index); + return 0; +} + +static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, + struct v4l2_dv_preset *qpreset) +{ + int index; + int err = tvp7002_query_dv(sd, &index); + + if (err || index == NUM_PRESETS) { + qpreset->preset = V4L2_DV_INVALID; + if (err == -ENOLINK) + err = 0; + return err; + } + qpreset->preset = tvp7002_presets[index].preset; + return 0; +} + +static int tvp7002_query_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + int index; + int err = tvp7002_query_dv(sd, &index); + + if (err) + return err; + *timings = tvp7002_presets[index].timings; + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +/* + * tvp7002_g_register() - Get the value of a register + * @sd: ptr to v4l2_subdev struct + * @reg: ptr to v4l2_dbg_register struct + * + * Get the value of a TVP7002 decoder device register. + * Returns zero when successful, -EINVAL if register read fails or + * access to I2C client fails, -EPERM if the call is not allowed + * by disabled CAP_SYS_ADMIN. + */ +static int tvp7002_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 val; + int ret; + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + ret = tvp7002_read(sd, reg->reg & 0xff, &val); + reg->val = val; + return ret; +} + +/* + * tvp7002_s_register() - set a control + * @sd: ptr to v4l2_subdev struct + * @reg: ptr to v4l2_dbg_register struct + * + * Get the value of a TVP7002 decoder device register. + * Returns zero when successful, -EINVAL if register read fails or + * -EPERM if call not allowed. + */ +static int tvp7002_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + return tvp7002_write(sd, reg->reg & 0xff, reg->val & 0xff); +} +#endif + +/* + * tvp7002_enum_mbus_fmt() - Enum supported mediabus formats + * @sd: pointer to standard V4L2 sub-device structure + * @index: format index + * @code: pointer to mediabus format + * + * Enumerate supported mediabus formats. + */ + +static int tvp7002_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, + enum v4l2_mbus_pixelcode *code) +{ + /* Check requested format index is within range */ + if (index) + return -EINVAL; + *code = V4L2_MBUS_FMT_YUYV10_1X20; + return 0; +} + +/* + * tvp7002_s_stream() - V4L2 decoder i/f handler for s_stream + * @sd: pointer to standard V4L2 sub-device structure + * @enable: streaming enable or disable + * + * Sets streaming to enable or disable, if possible. + */ +static int tvp7002_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct tvp7002 *device = to_tvp7002(sd); + int error = 0; + + if (device->streaming == enable) + return 0; + + if (enable) { + /* Set output state on (low impedance means stream on) */ + error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x00); + device->streaming = enable; + } else { + /* Set output state off (high impedance means stream off) */ + error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x03); + if (error) + v4l2_dbg(1, debug, sd, "Unable to stop streaming\n"); + + device->streaming = enable; + } + + return error; +} + +/* + * tvp7002_log_status() - Print information about register settings + * @sd: ptr to v4l2_subdev struct + * + * Log register values of a TVP7002 decoder device. + * Returns zero or -EINVAL if read operation fails. + */ +static int tvp7002_log_status(struct v4l2_subdev *sd) +{ + const struct tvp7002_preset_definition *presets = tvp7002_presets; + struct tvp7002 *device = to_tvp7002(sd); + struct v4l2_dv_enum_preset e_preset; + struct v4l2_dv_preset detected; + int i; + + detected.preset = V4L2_DV_INVALID; + /* Find my current standard*/ + tvp7002_query_dv_preset(sd, &detected); + + /* Print standard related code values */ + for (i = 0; i < NUM_PRESETS; i++, presets++) + if (presets->preset == detected.preset) + break; + + if (v4l_fill_dv_preset_info(device->current_preset->preset, &e_preset)) + return -EINVAL; + + v4l2_info(sd, "Selected DV Preset: %s\n", e_preset.name); + v4l2_info(sd, " Pixels per line: %u\n", e_preset.width); + v4l2_info(sd, " Lines per frame: %u\n\n", e_preset.height); + if (i == NUM_PRESETS) { + v4l2_info(sd, "Detected DV Preset: None\n"); + } else { + if (v4l_fill_dv_preset_info(presets->preset, &e_preset)) + return -EINVAL; + v4l2_info(sd, "Detected DV Preset: %s\n", e_preset.name); + v4l2_info(sd, " Pixels per line: %u\n", e_preset.width); + v4l2_info(sd, " Lines per frame: %u\n\n", e_preset.height); + } + v4l2_info(sd, "Streaming enabled: %s\n", + device->streaming ? "yes" : "no"); + + /* Print the current value of the gain control */ + v4l2_ctrl_handler_log_status(&device->hdl, sd->name); + + return 0; +} + +/* + * tvp7002_enum_dv_presets() - Enum supported digital video formats + * @sd: pointer to standard V4L2 sub-device structure + * @preset: pointer to format struct + * + * Enumerate supported digital video formats. + */ +static int tvp7002_enum_dv_presets(struct v4l2_subdev *sd, + struct v4l2_dv_enum_preset *preset) +{ + /* Check requested format index is within range */ + if (preset->index >= NUM_PRESETS) + return -EINVAL; + + return v4l_fill_dv_preset_info(tvp7002_presets[preset->index].preset, preset); +} + +static int tvp7002_enum_dv_timings(struct v4l2_subdev *sd, + struct v4l2_enum_dv_timings *timings) +{ + /* Check requested format index is within range */ + if (timings->index >= NUM_PRESETS) + return -EINVAL; + + timings->timings = tvp7002_presets[timings->index].timings; + return 0; +} + +static const struct v4l2_ctrl_ops tvp7002_ctrl_ops = { + .s_ctrl = tvp7002_s_ctrl, +}; + +/* V4L2 core operation handlers */ +static const struct v4l2_subdev_core_ops tvp7002_core_ops = { + .g_chip_ident = tvp7002_g_chip_ident, + .log_status = tvp7002_log_status, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = tvp7002_g_register, + .s_register = tvp7002_s_register, +#endif +}; + +/* Specific video subsystem operation handlers */ +static const struct v4l2_subdev_video_ops tvp7002_video_ops = { + .enum_dv_presets = tvp7002_enum_dv_presets, + .s_dv_preset = tvp7002_s_dv_preset, + .query_dv_preset = tvp7002_query_dv_preset, + .g_dv_timings = tvp7002_g_dv_timings, + .s_dv_timings = tvp7002_s_dv_timings, + .enum_dv_timings = tvp7002_enum_dv_timings, + .query_dv_timings = tvp7002_query_dv_timings, + .s_stream = tvp7002_s_stream, + .g_mbus_fmt = tvp7002_mbus_fmt, + .try_mbus_fmt = tvp7002_mbus_fmt, + .s_mbus_fmt = tvp7002_mbus_fmt, + .enum_mbus_fmt = tvp7002_enum_mbus_fmt, +}; + +/* V4L2 top level operation handlers */ +static const struct v4l2_subdev_ops tvp7002_ops = { + .core = &tvp7002_core_ops, + .video = &tvp7002_video_ops, +}; + +/* + * tvp7002_probe - Probe a TVP7002 device + * @c: ptr to i2c_client struct + * @id: ptr to i2c_device_id struct + * + * Initialize the TVP7002 device + * Returns zero when successful, -EINVAL if register read fails or + * -EIO if i2c access is not available. + */ +static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id) +{ + struct v4l2_subdev *sd; + struct tvp7002 *device; + struct v4l2_dv_preset preset; + int polarity_a; + int polarity_b; + u8 revision; + + int error; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(c->adapter, + I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return -EIO; + + if (!c->dev.platform_data) { + v4l_err(c, "No platform data!!\n"); + return -ENODEV; + } + + device = kzalloc(sizeof(struct tvp7002), GFP_KERNEL); + + if (!device) + return -ENOMEM; + + sd = &device->sd; + device->pdata = c->dev.platform_data; + device->current_preset = tvp7002_presets; + + /* Tell v4l2 the device is ready */ + v4l2_i2c_subdev_init(sd, c, &tvp7002_ops); + v4l_info(c, "tvp7002 found @ 0x%02x (%s)\n", + c->addr, c->adapter->name); + + error = tvp7002_read(sd, TVP7002_CHIP_REV, &revision); + if (error < 0) + goto found_error; + + /* Get revision number */ + v4l2_info(sd, "Rev. %02x detected.\n", revision); + if (revision != 0x02) + v4l2_info(sd, "Unknown revision detected.\n"); + + /* Initializes TVP7002 to its default values */ + error = tvp7002_write_inittab(sd, tvp7002_init_default); + + if (error < 0) + goto found_error; + + /* Set polarity information after registers have been set */ + polarity_a = 0x20 | device->pdata->hs_polarity << 5 + | device->pdata->vs_polarity << 2; + error = tvp7002_write(sd, TVP7002_SYNC_CTL_1, polarity_a); + if (error < 0) + goto found_error; + + polarity_b = 0x01 | device->pdata->fid_polarity << 2 + | device->pdata->sog_polarity << 1 + | device->pdata->clk_polarity; + error = tvp7002_write(sd, TVP7002_MISC_CTL_3, polarity_b); + if (error < 0) + goto found_error; + + /* Set registers according to default video mode */ + preset.preset = device->current_preset->preset; + error = tvp7002_s_dv_preset(sd, &preset); + + v4l2_ctrl_handler_init(&device->hdl, 1); + v4l2_ctrl_new_std(&device->hdl, &tvp7002_ctrl_ops, + V4L2_CID_GAIN, 0, 255, 1, 0); + sd->ctrl_handler = &device->hdl; + if (device->hdl.error) { + int err = device->hdl.error; + + v4l2_ctrl_handler_free(&device->hdl); + kfree(device); + return err; + } + v4l2_ctrl_handler_setup(&device->hdl); + +found_error: + if (error < 0) + kfree(device); + + return error; +} + +/* + * tvp7002_remove - Remove TVP7002 device support + * @c: ptr to i2c_client struct + * + * Reset the TVP7002 device + * Returns zero. + */ +static int tvp7002_remove(struct i2c_client *c) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(c); + struct tvp7002 *device = to_tvp7002(sd); + + v4l2_dbg(1, debug, sd, "Removing tvp7002 adapter" + "on address 0x%x\n", c->addr); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&device->hdl); + kfree(device); + return 0; +} + +/* I2C Device ID table */ +static const struct i2c_device_id tvp7002_id[] = { + { "tvp7002", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tvp7002_id); + +/* I2C driver data */ +static struct i2c_driver tvp7002_driver = { + .driver = { + .owner = THIS_MODULE, + .name = TVP7002_MODULE_NAME, + }, + .probe = tvp7002_probe, + .remove = tvp7002_remove, + .id_table = tvp7002_id, +}; + +module_i2c_driver(tvp7002_driver); diff --git a/drivers/media/i2c/tvp7002_reg.h b/drivers/media/i2c/tvp7002_reg.h new file mode 100644 index 00000000000..0e34ca9bccf --- /dev/null +++ b/drivers/media/i2c/tvp7002_reg.h @@ -0,0 +1,150 @@ +/* Texas Instruments Triple 8-/10-BIT 165-/110-MSPS Video and Graphics + * Digitizer with Horizontal PLL registers + * + * Copyright (C) 2009 Texas Instruments Inc + * Author: Santiago Nunez-Corrales + * + * This code is partially based upon the TVP5150 driver + * written by Mauro Carvalho Chehab (mchehab@infradead.org), + * the TVP514x driver written by Vaibhav Hiremath + * and the TVP7002 driver in the TI LSP 2.10.00.14 + * + * 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. + */ + +/* Naming conventions + * ------------------ + * + * FDBK: Feedback + * DIV: Divider + * CTL: Control + * SEL: Select + * IN: Input + * OUT: Output + * R: Red + * G: Green + * B: Blue + * OFF: Offset + * THRS: Threshold + * DGTL: Digital + * LVL: Level + * PWR: Power + * MVIS: Macrovision + * W: Width + * H: Height + * ALGN: Alignment + * CLK: Clocks + * TOL: Tolerance + * BWTH: Bandwidth + * COEF: Coefficient + * STAT: Status + * AUTO: Automatic + * FLD: Field + * L: Line + */ + +#define TVP7002_CHIP_REV 0x00 +#define TVP7002_HPLL_FDBK_DIV_MSBS 0x01 +#define TVP7002_HPLL_FDBK_DIV_LSBS 0x02 +#define TVP7002_HPLL_CRTL 0x03 +#define TVP7002_HPLL_PHASE_SEL 0x04 +#define TVP7002_CLAMP_START 0x05 +#define TVP7002_CLAMP_W 0x06 +#define TVP7002_HSYNC_OUT_W 0x07 +#define TVP7002_B_FINE_GAIN 0x08 +#define TVP7002_G_FINE_GAIN 0x09 +#define TVP7002_R_FINE_GAIN 0x0a +#define TVP7002_B_FINE_OFF_MSBS 0x0b +#define TVP7002_G_FINE_OFF_MSBS 0x0c +#define TVP7002_R_FINE_OFF_MSBS 0x0d +#define TVP7002_SYNC_CTL_1 0x0e +#define TVP7002_HPLL_AND_CLAMP_CTL 0x0f +#define TVP7002_SYNC_ON_G_THRS 0x10 +#define TVP7002_SYNC_SEPARATOR_THRS 0x11 +#define TVP7002_HPLL_PRE_COAST 0x12 +#define TVP7002_HPLL_POST_COAST 0x13 +#define TVP7002_SYNC_DETECT_STAT 0x14 +#define TVP7002_OUT_FORMATTER 0x15 +#define TVP7002_MISC_CTL_1 0x16 +#define TVP7002_MISC_CTL_2 0x17 +#define TVP7002_MISC_CTL_3 0x18 +#define TVP7002_IN_MUX_SEL_1 0x19 +#define TVP7002_IN_MUX_SEL_2 0x1a +#define TVP7002_B_AND_G_COARSE_GAIN 0x1b +#define TVP7002_R_COARSE_GAIN 0x1c +#define TVP7002_FINE_OFF_LSBS 0x1d +#define TVP7002_B_COARSE_OFF 0x1e +#define TVP7002_G_COARSE_OFF 0x1f +#define TVP7002_R_COARSE_OFF 0x20 +#define TVP7002_HSOUT_OUT_START 0x21 +#define TVP7002_MISC_CTL_4 0x22 +#define TVP7002_B_DGTL_ALC_OUT_LSBS 0x23 +#define TVP7002_G_DGTL_ALC_OUT_LSBS 0x24 +#define TVP7002_R_DGTL_ALC_OUT_LSBS 0x25 +#define TVP7002_AUTO_LVL_CTL_ENABLE 0x26 +#define TVP7002_DGTL_ALC_OUT_MSBS 0x27 +#define TVP7002_AUTO_LVL_CTL_FILTER 0x28 +/* Reserved 0x29*/ +#define TVP7002_FINE_CLAMP_CTL 0x2a +#define TVP7002_PWR_CTL 0x2b +#define TVP7002_ADC_SETUP 0x2c +#define TVP7002_COARSE_CLAMP_CTL 0x2d +#define TVP7002_SOG_CLAMP 0x2e +#define TVP7002_RGB_COARSE_CLAMP_CTL 0x2f +#define TVP7002_SOG_COARSE_CLAMP_CTL 0x30 +#define TVP7002_ALC_PLACEMENT 0x31 +/* Reserved 0x32 */ +/* Reserved 0x33 */ +#define TVP7002_MVIS_STRIPPER_W 0x34 +#define TVP7002_VSYNC_ALGN 0x35 +#define TVP7002_SYNC_BYPASS 0x36 +#define TVP7002_L_FRAME_STAT_LSBS 0x37 +#define TVP7002_L_FRAME_STAT_MSBS 0x38 +#define TVP7002_CLK_L_STAT_LSBS 0x39 +#define TVP7002_CLK_L_STAT_MSBS 0x3a +#define TVP7002_HSYNC_W 0x3b +#define TVP7002_VSYNC_W 0x3c +#define TVP7002_L_LENGTH_TOL 0x3d +/* Reserved 0x3e */ +#define TVP7002_VIDEO_BWTH_CTL 0x3f +#define TVP7002_AVID_START_PIXEL_LSBS 0x40 +#define TVP7002_AVID_START_PIXEL_MSBS 0x41 +#define TVP7002_AVID_STOP_PIXEL_LSBS 0x42 +#define TVP7002_AVID_STOP_PIXEL_MSBS 0x43 +#define TVP7002_VBLK_F_0_START_L_OFF 0x44 +#define TVP7002_VBLK_F_1_START_L_OFF 0x45 +#define TVP7002_VBLK_F_0_DURATION 0x46 +#define TVP7002_VBLK_F_1_DURATION 0x47 +#define TVP7002_FBIT_F_0_START_L_OFF 0x48 +#define TVP7002_FBIT_F_1_START_L_OFF 0x49 +#define TVP7002_YUV_Y_G_COEF_LSBS 0x4a +#define TVP7002_YUV_Y_G_COEF_MSBS 0x4b +#define TVP7002_YUV_Y_B_COEF_LSBS 0x4c +#define TVP7002_YUV_Y_B_COEF_MSBS 0x4d +#define TVP7002_YUV_Y_R_COEF_LSBS 0x4e +#define TVP7002_YUV_Y_R_COEF_MSBS 0x4f +#define TVP7002_YUV_U_G_COEF_LSBS 0x50 +#define TVP7002_YUV_U_G_COEF_MSBS 0x51 +#define TVP7002_YUV_U_B_COEF_LSBS 0x52 +#define TVP7002_YUV_U_B_COEF_MSBS 0x53 +#define TVP7002_YUV_U_R_COEF_LSBS 0x54 +#define TVP7002_YUV_U_R_COEF_MSBS 0x55 +#define TVP7002_YUV_V_G_COEF_LSBS 0x56 +#define TVP7002_YUV_V_G_COEF_MSBS 0x57 +#define TVP7002_YUV_V_B_COEF_LSBS 0x58 +#define TVP7002_YUV_V_B_COEF_MSBS 0x59 +#define TVP7002_YUV_V_R_COEF_LSBS 0x5a +#define TVP7002_YUV_V_R_COEF_MSBS 0x5b + diff --git a/drivers/media/i2c/upd64031a.c b/drivers/media/i2c/upd64031a.c new file mode 100644 index 00000000000..1e744654209 --- /dev/null +++ b/drivers/media/i2c/upd64031a.c @@ -0,0 +1,274 @@ +/* + * upd64031A - NEC Electronics Ghost Reduction for NTSC in Japan + * + * 2003 by T.Adachi + * 2003 by Takeru KOMORIYA + * 2006 by Hans Verkuil + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +/* --------------------- read registers functions define -------------------- */ + +/* bit masks */ +#define GR_MODE_MASK 0xc0 +#define DIRECT_3DYCS_CONNECT_MASK 0xc0 +#define SYNC_CIRCUIT_MASK 0xa0 + +/* -------------------------------------------------------------------------- */ + +MODULE_DESCRIPTION("uPD64031A driver"); +MODULE_AUTHOR("T. Adachi, Takeru KOMORIYA, Hans Verkuil"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0644); + +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + + +enum { + R00 = 0, R01, R02, R03, R04, + R05, R06, R07, R08, R09, + R0A, R0B, R0C, R0D, R0E, R0F, + /* unused registers + R10, R11, R12, R13, R14, + R15, R16, R17, + */ + TOT_REGS +}; + +struct upd64031a_state { + struct v4l2_subdev sd; + u8 regs[TOT_REGS]; + u8 gr_mode; + u8 direct_3dycs_connect; + u8 ext_comp_sync; + u8 ext_vert_sync; +}; + +static inline struct upd64031a_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct upd64031a_state, sd); +} + +static u8 upd64031a_init[] = { + 0x00, 0xb8, 0x48, 0xd2, 0xe6, + 0x03, 0x10, 0x0b, 0xaf, 0x7f, + 0x00, 0x00, 0x1d, 0x5e, 0x00, + 0xd0 +}; + +/* ------------------------------------------------------------------------ */ + +static u8 upd64031a_read(struct v4l2_subdev *sd, u8 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 buf[2]; + + if (reg >= sizeof(buf)) + return 0xff; + i2c_master_recv(client, buf, 2); + return buf[reg]; +} + +/* ------------------------------------------------------------------------ */ + +static void upd64031a_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 buf[2]; + + buf[0] = reg; + buf[1] = val; + v4l2_dbg(1, debug, sd, "write reg: %02X val: %02X\n", reg, val); + if (i2c_master_send(client, buf, 2) != 2) + v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n", reg, val); +} + +/* ------------------------------------------------------------------------ */ + +/* The input changed due to new input or channel changed */ +static int upd64031a_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +{ + struct upd64031a_state *state = to_state(sd); + u8 reg = state->regs[R00]; + + v4l2_dbg(1, debug, sd, "changed input or channel\n"); + upd64031a_write(sd, R00, reg | 0x10); + upd64031a_write(sd, R00, reg & ~0x10); + return 0; +} + +/* ------------------------------------------------------------------------ */ + +static int upd64031a_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct upd64031a_state *state = to_state(sd); + u8 r00, r05, r08; + + state->gr_mode = (input & 3) << 6; + state->direct_3dycs_connect = (input & 0xc) << 4; + state->ext_comp_sync = + (input & UPD64031A_COMPOSITE_EXTERNAL) << 1; + state->ext_vert_sync = + (input & UPD64031A_VERTICAL_EXTERNAL) << 2; + r00 = (state->regs[R00] & ~GR_MODE_MASK) | state->gr_mode; + r05 = (state->regs[R00] & ~SYNC_CIRCUIT_MASK) | + state->ext_comp_sync | state->ext_vert_sync; + r08 = (state->regs[R08] & ~DIRECT_3DYCS_CONNECT_MASK) | + state->direct_3dycs_connect; + upd64031a_write(sd, R00, r00); + upd64031a_write(sd, R05, r05); + upd64031a_write(sd, R08, r08); + return upd64031a_s_frequency(sd, NULL); +} + +static int upd64031a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_UPD64031A, 0); +} + +static int upd64031a_log_status(struct v4l2_subdev *sd) +{ + v4l2_info(sd, "Status: SA00=0x%02x SA01=0x%02x\n", + upd64031a_read(sd, 0), upd64031a_read(sd, 1)); + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int upd64031a_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = upd64031a_read(sd, reg->reg & 0xff); + reg->size = 1; + return 0; +} + +static int upd64031a_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + upd64031a_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops upd64031a_core_ops = { + .log_status = upd64031a_log_status, + .g_chip_ident = upd64031a_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = upd64031a_g_register, + .s_register = upd64031a_s_register, +#endif +}; + +static const struct v4l2_subdev_tuner_ops upd64031a_tuner_ops = { + .s_frequency = upd64031a_s_frequency, +}; + +static const struct v4l2_subdev_video_ops upd64031a_video_ops = { + .s_routing = upd64031a_s_routing, +}; + +static const struct v4l2_subdev_ops upd64031a_ops = { + .core = &upd64031a_core_ops, + .tuner = &upd64031a_tuner_ops, + .video = &upd64031a_video_ops, +}; + +/* ------------------------------------------------------------------------ */ + +/* i2c implementation */ + +static int upd64031a_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct upd64031a_state *state; + struct v4l2_subdev *sd; + int i; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct upd64031a_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &upd64031a_ops); + memcpy(state->regs, upd64031a_init, sizeof(state->regs)); + state->gr_mode = UPD64031A_GR_ON << 6; + state->direct_3dycs_connect = UPD64031A_3DYCS_COMPOSITE << 4; + state->ext_comp_sync = state->ext_vert_sync = 0; + for (i = 0; i < TOT_REGS; i++) + upd64031a_write(sd, i, state->regs[i]); + return 0; +} + +static int upd64031a_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 upd64031a_id[] = { + { "upd64031a", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, upd64031a_id); + +static struct i2c_driver upd64031a_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "upd64031a", + }, + .probe = upd64031a_probe, + .remove = upd64031a_remove, + .id_table = upd64031a_id, +}; + +module_i2c_driver(upd64031a_driver); diff --git a/drivers/media/i2c/upd64083.c b/drivers/media/i2c/upd64083.c new file mode 100644 index 00000000000..75d6acc6201 --- /dev/null +++ b/drivers/media/i2c/upd64083.c @@ -0,0 +1,246 @@ +/* + * upd6408x - NEC Electronics 3-Dimensional Y/C separation driver + * + * 2003 by T.Adachi (tadachi@tadachi-net.com) + * 2003 by Takeru KOMORIYA + * 2006 by Hans Verkuil + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("uPD64083 driver"); +MODULE_AUTHOR("T. Adachi, Takeru KOMORIYA, Hans Verkuil"); +MODULE_LICENSE("GPL"); + +static bool debug; +module_param(debug, bool, 0644); + +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + + +enum { + R00 = 0, R01, R02, R03, R04, + R05, R06, R07, R08, R09, + R0A, R0B, R0C, R0D, R0E, R0F, + R10, R11, R12, R13, R14, + R15, R16, + TOT_REGS +}; + +struct upd64083_state { + struct v4l2_subdev sd; + u8 mode; + u8 ext_y_adc; + u8 regs[TOT_REGS]; +}; + +static inline struct upd64083_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct upd64083_state, sd); +} + +/* Initial values when used in combination with the + NEC upd64031a ghost reduction chip. */ +static u8 upd64083_init[] = { + 0x1f, 0x01, 0xa0, 0x2d, 0x29, /* we use EXCSS=0 */ + 0x36, 0xdd, 0x05, 0x56, 0x48, + 0x00, 0x3a, 0xa0, 0x05, 0x08, + 0x44, 0x60, 0x08, 0x52, 0xf8, + 0x53, 0x60, 0x10 +}; + +/* ------------------------------------------------------------------------ */ + +static void upd64083_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 buf[2]; + + buf[0] = reg; + buf[1] = val; + v4l2_dbg(1, debug, sd, "write reg: %02x val: %02x\n", reg, val); + if (i2c_master_send(client, buf, 2) != 2) + v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n", reg, val); +} + +/* ------------------------------------------------------------------------ */ + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static u8 upd64083_read(struct v4l2_subdev *sd, u8 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 buf[7]; + + if (reg >= sizeof(buf)) + return 0xff; + i2c_master_recv(client, buf, sizeof(buf)); + return buf[reg]; +} +#endif + +/* ------------------------------------------------------------------------ */ + +static int upd64083_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct upd64083_state *state = to_state(sd); + u8 r00, r02; + + if (input > 7 || (input & 6) == 6) + return -EINVAL; + state->mode = (input & 3) << 6; + state->ext_y_adc = (input & UPD64083_EXT_Y_ADC) << 3; + r00 = (state->regs[R00] & ~(3 << 6)) | state->mode; + r02 = (state->regs[R02] & ~(1 << 5)) | state->ext_y_adc; + upd64083_write(sd, R00, r00); + upd64083_write(sd, R02, r02); + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int upd64083_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = upd64083_read(sd, reg->reg & 0xff); + reg->size = 1; + return 0; +} + +static int upd64083_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + upd64083_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif + +static int upd64083_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_UPD64083, 0); +} + +static int upd64083_log_status(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 buf[7]; + + i2c_master_recv(client, buf, 7); + v4l2_info(sd, "Status: SA00=%02x SA01=%02x SA02=%02x SA03=%02x " + "SA04=%02x SA05=%02x SA06=%02x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops upd64083_core_ops = { + .log_status = upd64083_log_status, + .g_chip_ident = upd64083_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = upd64083_g_register, + .s_register = upd64083_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops upd64083_video_ops = { + .s_routing = upd64083_s_routing, +}; + +static const struct v4l2_subdev_ops upd64083_ops = { + .core = &upd64083_core_ops, + .video = &upd64083_video_ops, +}; + +/* ------------------------------------------------------------------------ */ + +/* i2c implementation */ + +static int upd64083_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct upd64083_state *state; + struct v4l2_subdev *sd; + int i; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct upd64083_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &upd64083_ops); + /* Initially assume that a ghost reduction chip is present */ + state->mode = 0; /* YCS mode */ + state->ext_y_adc = (1 << 5); + memcpy(state->regs, upd64083_init, TOT_REGS); + for (i = 0; i < TOT_REGS; i++) + upd64083_write(sd, i, state->regs[i]); + return 0; +} + +static int upd64083_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 upd64083_id[] = { + { "upd64083", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, upd64083_id); + +static struct i2c_driver upd64083_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "upd64083", + }, + .probe = upd64083_probe, + .remove = upd64083_remove, + .id_table = upd64083_id, +}; + +module_i2c_driver(upd64083_driver); diff --git a/drivers/media/i2c/vp27smpx.c b/drivers/media/i2c/vp27smpx.c new file mode 100644 index 00000000000..7cfbc9d94a4 --- /dev/null +++ b/drivers/media/i2c/vp27smpx.c @@ -0,0 +1,211 @@ +/* + * vp27smpx - driver version 0.0.1 + * + * Copyright (C) 2007 Hans Verkuil + * + * Based on a tvaudio patch from Takahiro Adachi + * and Kazuhiko Kawakami + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("vp27smpx driver"); +MODULE_AUTHOR("Hans Verkuil"); +MODULE_LICENSE("GPL"); + + +/* ----------------------------------------------------------------------- */ + +struct vp27smpx_state { + struct v4l2_subdev sd; + int radio; + u32 audmode; +}; + +static inline struct vp27smpx_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct vp27smpx_state, sd); +} + +static void vp27smpx_set_audmode(struct v4l2_subdev *sd, u32 audmode) +{ + struct vp27smpx_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 data[3] = { 0x00, 0x00, 0x04 }; + + switch (audmode) { + case V4L2_TUNER_MODE_MONO: + case V4L2_TUNER_MODE_LANG1: + break; + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1_LANG2: + data[1] = 0x01; + break; + case V4L2_TUNER_MODE_LANG2: + data[1] = 0x02; + break; + } + + if (i2c_master_send(client, data, sizeof(data)) != sizeof(data)) + v4l2_err(sd, "I/O error setting audmode\n"); + else + state->audmode = audmode; +} + +static int vp27smpx_s_radio(struct v4l2_subdev *sd) +{ + struct vp27smpx_state *state = to_state(sd); + + state->radio = 1; + return 0; +} + +static int vp27smpx_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + struct vp27smpx_state *state = to_state(sd); + + state->radio = 0; + return 0; +} + +static int vp27smpx_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct vp27smpx_state *state = to_state(sd); + + if (!state->radio) + vp27smpx_set_audmode(sd, vt->audmode); + return 0; +} + +static int vp27smpx_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct vp27smpx_state *state = to_state(sd); + + if (state->radio) + return 0; + vt->audmode = state->audmode; + vt->capability = V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + return 0; +} + +static int vp27smpx_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VP27SMPX, 0); +} + +static int vp27smpx_log_status(struct v4l2_subdev *sd) +{ + struct vp27smpx_state *state = to_state(sd); + + v4l2_info(sd, "Audio Mode: %u%s\n", state->audmode, + state->radio ? " (Radio)" : ""); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops vp27smpx_core_ops = { + .log_status = vp27smpx_log_status, + .g_chip_ident = vp27smpx_g_chip_ident, + .s_std = vp27smpx_s_std, +}; + +static const struct v4l2_subdev_tuner_ops vp27smpx_tuner_ops = { + .s_radio = vp27smpx_s_radio, + .s_tuner = vp27smpx_s_tuner, + .g_tuner = vp27smpx_g_tuner, +}; + +static const struct v4l2_subdev_ops vp27smpx_ops = { + .core = &vp27smpx_core_ops, + .tuner = &vp27smpx_tuner_ops, +}; + +/* ----------------------------------------------------------------------- */ + +/* i2c implementation */ + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ + +static int vp27smpx_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct vp27smpx_state *state; + struct v4l2_subdev *sd; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct vp27smpx_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &vp27smpx_ops); + state->audmode = V4L2_TUNER_MODE_STEREO; + + /* initialize vp27smpx */ + vp27smpx_set_audmode(sd, state->audmode); + return 0; +} + +static int vp27smpx_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 vp27smpx_id[] = { + { "vp27smpx", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, vp27smpx_id); + +static struct i2c_driver vp27smpx_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "vp27smpx", + }, + .probe = vp27smpx_probe, + .remove = vp27smpx_remove, + .id_table = vp27smpx_id, +}; + +module_i2c_driver(vp27smpx_driver); diff --git a/drivers/media/i2c/vpx3220.c b/drivers/media/i2c/vpx3220.c new file mode 100644 index 00000000000..2f67b4c5c82 --- /dev/null +++ b/drivers/media/i2c/vpx3220.c @@ -0,0 +1,591 @@ +/* + * vpx3220a, vpx3216b & vpx3214c video decoder driver version 0.0.1 + * + * Copyright (C) 2001 Laurent Pinchart + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video decoder driver"); +MODULE_AUTHOR("Laurent Pinchart"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + + +#define VPX_TIMEOUT_COUNT 10 + +/* ----------------------------------------------------------------------- */ + +struct vpx3220 { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + unsigned char reg[255]; + + v4l2_std_id norm; + int ident; + int input; + int enable; +}; + +static inline struct vpx3220 *to_vpx3220(struct v4l2_subdev *sd) +{ + return container_of(sd, struct vpx3220, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct vpx3220, hdl)->sd; +} + +static char *inputs[] = { "internal", "composite", "svideo" }; + +/* ----------------------------------------------------------------------- */ + +static inline int vpx3220_write(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct vpx3220 *decoder = i2c_get_clientdata(client); + + decoder->reg[reg] = value; + return i2c_smbus_write_byte_data(client, reg, value); +} + +static inline int vpx3220_read(struct v4l2_subdev *sd, u8 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_read_byte_data(client, reg); +} + +static int vpx3220_fp_status(struct v4l2_subdev *sd) +{ + unsigned char status; + unsigned int i; + + for (i = 0; i < VPX_TIMEOUT_COUNT; i++) { + status = vpx3220_read(sd, 0x29); + + if (!(status & 4)) + return 0; + + udelay(10); + + if (need_resched()) + cond_resched(); + } + + return -1; +} + +static int vpx3220_fp_write(struct v4l2_subdev *sd, u8 fpaddr, u16 data) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + /* Write the 16-bit address to the FPWR register */ + if (i2c_smbus_write_word_data(client, 0x27, swab16(fpaddr)) == -1) { + v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); + return -1; + } + + if (vpx3220_fp_status(sd) < 0) + return -1; + + /* Write the 16-bit data to the FPDAT register */ + if (i2c_smbus_write_word_data(client, 0x28, swab16(data)) == -1) { + v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); + return -1; + } + + return 0; +} + +static u16 vpx3220_fp_read(struct v4l2_subdev *sd, u16 fpaddr) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + s16 data; + + /* Write the 16-bit address to the FPRD register */ + if (i2c_smbus_write_word_data(client, 0x26, swab16(fpaddr)) == -1) { + v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); + return -1; + } + + if (vpx3220_fp_status(sd) < 0) + return -1; + + /* Read the 16-bit data from the FPDAT register */ + data = i2c_smbus_read_word_data(client, 0x28); + if (data == -1) { + v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); + return -1; + } + + return swab16(data); +} + +static int vpx3220_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len) +{ + u8 reg; + int ret = -1; + + while (len >= 2) { + reg = *data++; + ret = vpx3220_write(sd, reg, *data++); + if (ret < 0) + break; + len -= 2; + } + + return ret; +} + +static int vpx3220_write_fp_block(struct v4l2_subdev *sd, + const u16 *data, unsigned int len) +{ + u8 reg; + int ret = 0; + + while (len > 1) { + reg = *data++; + ret |= vpx3220_fp_write(sd, reg, *data++); + len -= 2; + } + + return ret; +} + +/* ---------------------------------------------------------------------- */ + +static const unsigned short init_ntsc[] = { + 0x1c, 0x00, /* NTSC tint angle */ + 0x88, 17, /* Window 1 vertical */ + 0x89, 240, /* Vertical lines in */ + 0x8a, 240, /* Vertical lines out */ + 0x8b, 000, /* Horizontal begin */ + 0x8c, 640, /* Horizontal length */ + 0x8d, 640, /* Number of pixels */ + 0x8f, 0xc00, /* Disable window 2 */ + 0xf0, 0x73, /* 13.5 MHz transport, Forced + * mode, latch windows */ + 0xf2, 0x13, /* NTSC M, composite input */ + 0xe7, 0x1e1, /* Enable vertical standard + * locking @ 240 lines */ +}; + +static const unsigned short init_pal[] = { + 0x88, 23, /* Window 1 vertical begin */ + 0x89, 288, /* Vertical lines in (16 lines + * skipped by the VFE) */ + 0x8a, 288, /* Vertical lines out (16 lines + * skipped by the VFE) */ + 0x8b, 16, /* Horizontal begin */ + 0x8c, 768, /* Horizontal length */ + 0x8d, 784, /* Number of pixels + * Must be >= Horizontal begin + Horizontal length */ + 0x8f, 0xc00, /* Disable window 2 */ + 0xf0, 0x77, /* 13.5 MHz transport, Forced + * mode, latch windows */ + 0xf2, 0x3d1, /* PAL B,G,H,I, composite input */ + 0xe7, 0x241, /* PAL/SECAM set to 288 lines */ +}; + +static const unsigned short init_secam[] = { + 0x88, 23, /* Window 1 vertical begin */ + 0x89, 288, /* Vertical lines in (16 lines + * skipped by the VFE) */ + 0x8a, 288, /* Vertical lines out (16 lines + * skipped by the VFE) */ + 0x8b, 16, /* Horizontal begin */ + 0x8c, 768, /* Horizontal length */ + 0x8d, 784, /* Number of pixels + * Must be >= Horizontal begin + Horizontal length */ + 0x8f, 0xc00, /* Disable window 2 */ + 0xf0, 0x77, /* 13.5 MHz transport, Forced + * mode, latch windows */ + 0xf2, 0x3d5, /* SECAM, composite input */ + 0xe7, 0x241, /* PAL/SECAM set to 288 lines */ +}; + +static const unsigned char init_common[] = { + 0xf2, 0x00, /* Disable all outputs */ + 0x33, 0x0d, /* Luma : VIN2, Chroma : CIN + * (clamp off) */ + 0xd8, 0xa8, /* HREF/VREF active high, VREF + * pulse = 2, Odd/Even flag */ + 0x20, 0x03, /* IF compensation 0dB/oct */ + 0xe0, 0xff, /* Open up all comparators */ + 0xe1, 0x00, + 0xe2, 0x7f, + 0xe3, 0x80, + 0xe4, 0x7f, + 0xe5, 0x80, + 0xe6, 0x00, /* Brightness set to 0 */ + 0xe7, 0xe0, /* Contrast to 1.0, noise shaping + * 10 to 8 2-bit error diffusion */ + 0xe8, 0xf8, /* YUV422, CbCr binary offset, + * ... (p.32) */ + 0xea, 0x18, /* LLC2 connected, output FIFO + * reset with VACTintern */ + 0xf0, 0x8a, /* Half full level to 10, bus + * shuffler [7:0, 23:16, 15:8] */ + 0xf1, 0x18, /* Single clock, sync mode, no + * FE delay, no HLEN counter */ + 0xf8, 0x12, /* Port A, PIXCLK, HF# & FE# + * strength to 2 */ + 0xf9, 0x24, /* Port B, HREF, VREF, PREF & + * ALPHA strength to 4 */ +}; + +static const unsigned short init_fp[] = { + 0x59, 0, + 0xa0, 2070, /* ACC reference */ + 0xa3, 0, + 0xa4, 0, + 0xa8, 30, + 0xb2, 768, + 0xbe, 27, + 0x58, 0, + 0x26, 0, + 0x4b, 0x298, /* PLL gain */ +}; + + +static int vpx3220_init(struct v4l2_subdev *sd, u32 val) +{ + struct vpx3220 *decoder = to_vpx3220(sd); + + vpx3220_write_block(sd, init_common, sizeof(init_common)); + vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1); + if (decoder->norm & V4L2_STD_NTSC) + vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1); + else if (decoder->norm & V4L2_STD_PAL) + vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); + else if (decoder->norm & V4L2_STD_SECAM) + vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1); + else + vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); + return 0; +} + +static int vpx3220_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd) +{ + int res = V4L2_IN_ST_NO_SIGNAL, status; + v4l2_std_id std = 0; + + status = vpx3220_fp_read(sd, 0x0f3); + + v4l2_dbg(1, debug, sd, "status: 0x%04x\n", status); + + if (status < 0) + return status; + + if ((status & 0x20) == 0) { + res = 0; + + switch (status & 0x18) { + case 0x00: + case 0x10: + case 0x14: + case 0x18: + std = V4L2_STD_PAL; + break; + + case 0x08: + std = V4L2_STD_SECAM; + break; + + case 0x04: + case 0x0c: + case 0x1c: + std = V4L2_STD_NTSC; + break; + } + } + if (pstd) + *pstd = std; + if (pstatus) + *pstatus = res; + return 0; +} + +static int vpx3220_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + v4l2_dbg(1, debug, sd, "querystd\n"); + return vpx3220_status(sd, NULL, std); +} + +static int vpx3220_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + v4l2_dbg(1, debug, sd, "g_input_status\n"); + return vpx3220_status(sd, status, NULL); +} + +static int vpx3220_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct vpx3220 *decoder = to_vpx3220(sd); + int temp_input; + + /* Here we back up the input selection because it gets + overwritten when we fill the registers with the + chosen video norm */ + temp_input = vpx3220_fp_read(sd, 0xf2); + + v4l2_dbg(1, debug, sd, "s_std %llx\n", (unsigned long long)std); + if (std & V4L2_STD_NTSC) { + vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1); + v4l2_dbg(1, debug, sd, "norm switched to NTSC\n"); + } else if (std & V4L2_STD_PAL) { + vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); + v4l2_dbg(1, debug, sd, "norm switched to PAL\n"); + } else if (std & V4L2_STD_SECAM) { + vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1); + v4l2_dbg(1, debug, sd, "norm switched to SECAM\n"); + } else { + return -EINVAL; + } + + decoder->norm = std; + + /* And here we set the backed up video input again */ + vpx3220_fp_write(sd, 0xf2, temp_input | 0x0010); + udelay(10); + return 0; +} + +static int vpx3220_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + int data; + + /* RJ: input = 0: ST8 (PCTV) input + input = 1: COMPOSITE input + input = 2: SVHS input */ + + const int input_vals[3][2] = { + {0x0c, 0}, + {0x0d, 0}, + {0x0e, 1} + }; + + if (input > 2) + return -EINVAL; + + v4l2_dbg(1, debug, sd, "input switched to %s\n", inputs[input]); + + vpx3220_write(sd, 0x33, input_vals[input][0]); + + data = vpx3220_fp_read(sd, 0xf2) & ~(0x0020); + if (data < 0) + return data; + /* 0x0010 is required to latch the setting */ + vpx3220_fp_write(sd, 0xf2, + data | (input_vals[input][1] << 5) | 0x0010); + + udelay(10); + return 0; +} + +static int vpx3220_s_stream(struct v4l2_subdev *sd, int enable) +{ + v4l2_dbg(1, debug, sd, "s_stream %s\n", enable ? "on" : "off"); + + vpx3220_write(sd, 0xf2, (enable ? 0x1b : 0x00)); + return 0; +} + +static int vpx3220_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + vpx3220_write(sd, 0xe6, ctrl->val); + return 0; + case V4L2_CID_CONTRAST: + /* Bit 7 and 8 is for noise shaping */ + vpx3220_write(sd, 0xe7, ctrl->val + 192); + return 0; + case V4L2_CID_SATURATION: + vpx3220_fp_write(sd, 0xa0, ctrl->val); + return 0; + case V4L2_CID_HUE: + vpx3220_fp_write(sd, 0x1c, ctrl->val); + return 0; + } + return -EINVAL; +} + +static int vpx3220_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct vpx3220 *decoder = to_vpx3220(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, decoder->ident, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops vpx3220_ctrl_ops = { + .s_ctrl = vpx3220_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops vpx3220_core_ops = { + .g_chip_ident = vpx3220_g_chip_ident, + .init = vpx3220_init, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, + .s_std = vpx3220_s_std, +}; + +static const struct v4l2_subdev_video_ops vpx3220_video_ops = { + .s_routing = vpx3220_s_routing, + .s_stream = vpx3220_s_stream, + .querystd = vpx3220_querystd, + .g_input_status = vpx3220_g_input_status, +}; + +static const struct v4l2_subdev_ops vpx3220_ops = { + .core = &vpx3220_core_ops, + .video = &vpx3220_video_ops, +}; + +/* ----------------------------------------------------------------------- + * Client management code + */ + +static int vpx3220_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct vpx3220 *decoder; + struct v4l2_subdev *sd; + const char *name = NULL; + u8 ver; + u16 pn; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + decoder = kzalloc(sizeof(struct vpx3220), GFP_KERNEL); + if (decoder == NULL) + return -ENOMEM; + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &vpx3220_ops); + decoder->norm = V4L2_STD_PAL; + decoder->input = 0; + decoder->enable = 1; + v4l2_ctrl_handler_init(&decoder->hdl, 4); + v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, + V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); + v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, + V4L2_CID_CONTRAST, 0, 63, 1, 32); + v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, + V4L2_CID_SATURATION, 0, 4095, 1, 2048); + v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, + V4L2_CID_HUE, -512, 511, 1, 0); + sd->ctrl_handler = &decoder->hdl; + if (decoder->hdl.error) { + int err = decoder->hdl.error; + + v4l2_ctrl_handler_free(&decoder->hdl); + kfree(decoder); + return err; + } + v4l2_ctrl_handler_setup(&decoder->hdl); + + ver = i2c_smbus_read_byte_data(client, 0x00); + pn = (i2c_smbus_read_byte_data(client, 0x02) << 8) + + i2c_smbus_read_byte_data(client, 0x01); + decoder->ident = V4L2_IDENT_VPX3220A; + if (ver == 0xec) { + switch (pn) { + case 0x4680: + name = "vpx3220a"; + break; + case 0x4260: + name = "vpx3216b"; + decoder->ident = V4L2_IDENT_VPX3216B; + break; + case 0x4280: + name = "vpx3214c"; + decoder->ident = V4L2_IDENT_VPX3214C; + break; + } + } + if (name) + v4l2_info(sd, "%s found @ 0x%x (%s)\n", name, + client->addr << 1, client->adapter->name); + else + v4l2_info(sd, "chip (%02x:%04x) found @ 0x%x (%s)\n", + ver, pn, client->addr << 1, client->adapter->name); + + vpx3220_write_block(sd, init_common, sizeof(init_common)); + vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1); + /* Default to PAL */ + vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); + return 0; +} + +static int vpx3220_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct vpx3220 *decoder = to_vpx3220(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&decoder->hdl); + kfree(decoder); + return 0; +} + +static const struct i2c_device_id vpx3220_id[] = { + { "vpx3220a", 0 }, + { "vpx3216b", 0 }, + { "vpx3214c", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, vpx3220_id); + +static struct i2c_driver vpx3220_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "vpx3220", + }, + .probe = vpx3220_probe, + .remove = vpx3220_remove, + .id_table = vpx3220_id, +}; + +module_i2c_driver(vpx3220_driver); diff --git a/drivers/media/i2c/vs6624.c b/drivers/media/i2c/vs6624.c new file mode 100644 index 00000000000..42ae9dc9c57 --- /dev/null +++ b/drivers/media/i2c/vs6624.c @@ -0,0 +1,928 @@ +/* + * vs6624.c ST VS6624 CMOS image sensor driver + * + * Copyright (c) 2011 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "vs6624_regs.h" + +#define VGA_WIDTH 640 +#define VGA_HEIGHT 480 +#define QVGA_WIDTH 320 +#define QVGA_HEIGHT 240 +#define QQVGA_WIDTH 160 +#define QQVGA_HEIGHT 120 +#define CIF_WIDTH 352 +#define CIF_HEIGHT 288 +#define QCIF_WIDTH 176 +#define QCIF_HEIGHT 144 +#define QQCIF_WIDTH 88 +#define QQCIF_HEIGHT 72 + +#define MAX_FRAME_RATE 30 + +struct vs6624 { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + struct v4l2_fract frame_rate; + struct v4l2_mbus_framefmt fmt; + unsigned ce_pin; +}; + +static const struct vs6624_format { + enum v4l2_mbus_pixelcode mbus_code; + enum v4l2_colorspace colorspace; +} vs6624_formats[] = { + { + .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + }, + { + .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + }, + { + .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE, + .colorspace = V4L2_COLORSPACE_SRGB, + }, +}; + +static struct v4l2_mbus_framefmt vs6624_default_fmt = { + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .code = V4L2_MBUS_FMT_UYVY8_2X8, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_JPEG, +}; + +static const u16 vs6624_p1[] = { + 0x8104, 0x03, + 0x8105, 0x01, + 0xc900, 0x03, + 0xc904, 0x47, + 0xc905, 0x10, + 0xc906, 0x80, + 0xc907, 0x3a, + 0x903a, 0x02, + 0x903b, 0x47, + 0x903c, 0x15, + 0xc908, 0x31, + 0xc909, 0xdc, + 0xc90a, 0x80, + 0xc90b, 0x44, + 0x9044, 0x02, + 0x9045, 0x31, + 0x9046, 0xe2, + 0xc90c, 0x07, + 0xc90d, 0xe0, + 0xc90e, 0x80, + 0xc90f, 0x47, + 0x9047, 0x90, + 0x9048, 0x83, + 0x9049, 0x81, + 0x904a, 0xe0, + 0x904b, 0x60, + 0x904c, 0x08, + 0x904d, 0x90, + 0x904e, 0xc0, + 0x904f, 0x43, + 0x9050, 0x74, + 0x9051, 0x01, + 0x9052, 0xf0, + 0x9053, 0x80, + 0x9054, 0x05, + 0x9055, 0xE4, + 0x9056, 0x90, + 0x9057, 0xc0, + 0x9058, 0x43, + 0x9059, 0xf0, + 0x905a, 0x02, + 0x905b, 0x07, + 0x905c, 0xec, + 0xc910, 0x5d, + 0xc911, 0xca, + 0xc912, 0x80, + 0xc913, 0x5d, + 0x905d, 0xa3, + 0x905e, 0x04, + 0x905f, 0xf0, + 0x9060, 0xa3, + 0x9061, 0x04, + 0x9062, 0xf0, + 0x9063, 0x22, + 0xc914, 0x72, + 0xc915, 0x92, + 0xc916, 0x80, + 0xc917, 0x64, + 0x9064, 0x74, + 0x9065, 0x01, + 0x9066, 0x02, + 0x9067, 0x72, + 0x9068, 0x95, + 0xc918, 0x47, + 0xc919, 0xf2, + 0xc91a, 0x81, + 0xc91b, 0x69, + 0x9169, 0x74, + 0x916a, 0x02, + 0x916b, 0xf0, + 0x916c, 0xec, + 0x916d, 0xb4, + 0x916e, 0x10, + 0x916f, 0x0a, + 0x9170, 0x90, + 0x9171, 0x80, + 0x9172, 0x16, + 0x9173, 0xe0, + 0x9174, 0x70, + 0x9175, 0x04, + 0x9176, 0x90, + 0x9177, 0xd3, + 0x9178, 0xc4, + 0x9179, 0xf0, + 0x917a, 0x22, + 0xc91c, 0x0a, + 0xc91d, 0xbe, + 0xc91e, 0x80, + 0xc91f, 0x73, + 0x9073, 0xfc, + 0x9074, 0xa3, + 0x9075, 0xe0, + 0x9076, 0xf5, + 0x9077, 0x82, + 0x9078, 0x8c, + 0x9079, 0x83, + 0x907a, 0xa3, + 0x907b, 0xa3, + 0x907c, 0xe0, + 0x907d, 0xfc, + 0x907e, 0xa3, + 0x907f, 0xe0, + 0x9080, 0xc3, + 0x9081, 0x9f, + 0x9082, 0xff, + 0x9083, 0xec, + 0x9084, 0x9e, + 0x9085, 0xfe, + 0x9086, 0x02, + 0x9087, 0x0a, + 0x9088, 0xea, + 0xc920, 0x47, + 0xc921, 0x38, + 0xc922, 0x80, + 0xc923, 0x89, + 0x9089, 0xec, + 0x908a, 0xd3, + 0x908b, 0x94, + 0x908c, 0x20, + 0x908d, 0x40, + 0x908e, 0x01, + 0x908f, 0x1c, + 0x9090, 0x90, + 0x9091, 0xd3, + 0x9092, 0xd4, + 0x9093, 0xec, + 0x9094, 0xf0, + 0x9095, 0x02, + 0x9096, 0x47, + 0x9097, 0x3d, + 0xc924, 0x45, + 0xc925, 0xca, + 0xc926, 0x80, + 0xc927, 0x98, + 0x9098, 0x12, + 0x9099, 0x77, + 0x909a, 0xd6, + 0x909b, 0x02, + 0x909c, 0x45, + 0x909d, 0xcd, + 0xc928, 0x20, + 0xc929, 0xd5, + 0xc92a, 0x80, + 0xc92b, 0x9e, + 0x909e, 0x90, + 0x909f, 0x82, + 0x90a0, 0x18, + 0x90a1, 0xe0, + 0x90a2, 0xb4, + 0x90a3, 0x03, + 0x90a4, 0x0e, + 0x90a5, 0x90, + 0x90a6, 0x83, + 0x90a7, 0xbf, + 0x90a8, 0xe0, + 0x90a9, 0x60, + 0x90aa, 0x08, + 0x90ab, 0x90, + 0x90ac, 0x81, + 0x90ad, 0xfc, + 0x90ae, 0xe0, + 0x90af, 0xff, + 0x90b0, 0xc3, + 0x90b1, 0x13, + 0x90b2, 0xf0, + 0x90b3, 0x90, + 0x90b4, 0x81, + 0x90b5, 0xfc, + 0x90b6, 0xe0, + 0x90b7, 0xff, + 0x90b8, 0x02, + 0x90b9, 0x20, + 0x90ba, 0xda, + 0xc92c, 0x70, + 0xc92d, 0xbc, + 0xc92e, 0x80, + 0xc92f, 0xbb, + 0x90bb, 0x90, + 0x90bc, 0x82, + 0x90bd, 0x18, + 0x90be, 0xe0, + 0x90bf, 0xb4, + 0x90c0, 0x03, + 0x90c1, 0x06, + 0x90c2, 0x90, + 0x90c3, 0xc1, + 0x90c4, 0x06, + 0x90c5, 0x74, + 0x90c6, 0x05, + 0x90c7, 0xf0, + 0x90c8, 0x90, + 0x90c9, 0xd3, + 0x90ca, 0xa0, + 0x90cb, 0x02, + 0x90cc, 0x70, + 0x90cd, 0xbf, + 0xc930, 0x72, + 0xc931, 0x21, + 0xc932, 0x81, + 0xc933, 0x3b, + 0x913b, 0x7d, + 0x913c, 0x02, + 0x913d, 0x7f, + 0x913e, 0x7b, + 0x913f, 0x02, + 0x9140, 0x72, + 0x9141, 0x25, + 0xc934, 0x28, + 0xc935, 0xae, + 0xc936, 0x80, + 0xc937, 0xd2, + 0x90d2, 0xf0, + 0x90d3, 0x90, + 0x90d4, 0xd2, + 0x90d5, 0x0a, + 0x90d6, 0x02, + 0x90d7, 0x28, + 0x90d8, 0xb4, + 0xc938, 0x28, + 0xc939, 0xb1, + 0xc93a, 0x80, + 0xc93b, 0xd9, + 0x90d9, 0x90, + 0x90da, 0x83, + 0x90db, 0xba, + 0x90dc, 0xe0, + 0x90dd, 0xff, + 0x90de, 0x90, + 0x90df, 0xd2, + 0x90e0, 0x08, + 0x90e1, 0xe0, + 0x90e2, 0xe4, + 0x90e3, 0xef, + 0x90e4, 0xf0, + 0x90e5, 0xa3, + 0x90e6, 0xe0, + 0x90e7, 0x74, + 0x90e8, 0xff, + 0x90e9, 0xf0, + 0x90ea, 0x90, + 0x90eb, 0xd2, + 0x90ec, 0x0a, + 0x90ed, 0x02, + 0x90ee, 0x28, + 0x90ef, 0xb4, + 0xc93c, 0x29, + 0xc93d, 0x79, + 0xc93e, 0x80, + 0xc93f, 0xf0, + 0x90f0, 0xf0, + 0x90f1, 0x90, + 0x90f2, 0xd2, + 0x90f3, 0x0e, + 0x90f4, 0x02, + 0x90f5, 0x29, + 0x90f6, 0x7f, + 0xc940, 0x29, + 0xc941, 0x7c, + 0xc942, 0x80, + 0xc943, 0xf7, + 0x90f7, 0x90, + 0x90f8, 0x83, + 0x90f9, 0xba, + 0x90fa, 0xe0, + 0x90fb, 0xff, + 0x90fc, 0x90, + 0x90fd, 0xd2, + 0x90fe, 0x0c, + 0x90ff, 0xe0, + 0x9100, 0xe4, + 0x9101, 0xef, + 0x9102, 0xf0, + 0x9103, 0xa3, + 0x9104, 0xe0, + 0x9105, 0x74, + 0x9106, 0xff, + 0x9107, 0xf0, + 0x9108, 0x90, + 0x9109, 0xd2, + 0x910a, 0x0e, + 0x910b, 0x02, + 0x910c, 0x29, + 0x910d, 0x7f, + 0xc944, 0x2a, + 0xc945, 0x42, + 0xc946, 0x81, + 0xc947, 0x0e, + 0x910e, 0xf0, + 0x910f, 0x90, + 0x9110, 0xd2, + 0x9111, 0x12, + 0x9112, 0x02, + 0x9113, 0x2a, + 0x9114, 0x48, + 0xc948, 0x2a, + 0xc949, 0x45, + 0xc94a, 0x81, + 0xc94b, 0x15, + 0x9115, 0x90, + 0x9116, 0x83, + 0x9117, 0xba, + 0x9118, 0xe0, + 0x9119, 0xff, + 0x911a, 0x90, + 0x911b, 0xd2, + 0x911c, 0x10, + 0x911d, 0xe0, + 0x911e, 0xe4, + 0x911f, 0xef, + 0x9120, 0xf0, + 0x9121, 0xa3, + 0x9122, 0xe0, + 0x9123, 0x74, + 0x9124, 0xff, + 0x9125, 0xf0, + 0x9126, 0x90, + 0x9127, 0xd2, + 0x9128, 0x12, + 0x9129, 0x02, + 0x912a, 0x2a, + 0x912b, 0x48, + 0xc900, 0x01, + 0x0000, 0x00, +}; + +static const u16 vs6624_p2[] = { + 0x806f, 0x01, + 0x058c, 0x01, + 0x0000, 0x00, +}; + +static const u16 vs6624_run_setup[] = { + 0x1d18, 0x00, /* Enableconstrainedwhitebalance */ + VS6624_PEAK_MIN_OUT_G_MSB, 0x3c, /* Damper PeakGain Output MSB */ + VS6624_PEAK_MIN_OUT_G_LSB, 0x66, /* Damper PeakGain Output LSB */ + VS6624_CM_LOW_THR_MSB, 0x65, /* Damper Low MSB */ + VS6624_CM_LOW_THR_LSB, 0xd1, /* Damper Low LSB */ + VS6624_CM_HIGH_THR_MSB, 0x66, /* Damper High MSB */ + VS6624_CM_HIGH_THR_LSB, 0x62, /* Damper High LSB */ + VS6624_CM_MIN_OUT_MSB, 0x00, /* Damper Min output MSB */ + VS6624_CM_MIN_OUT_LSB, 0x00, /* Damper Min output LSB */ + VS6624_NORA_DISABLE, 0x00, /* Nora fDisable */ + VS6624_NORA_USAGE, 0x04, /* Nora usage */ + VS6624_NORA_LOW_THR_MSB, 0x63, /* Damper Low MSB Changed 0x63 to 0x65 */ + VS6624_NORA_LOW_THR_LSB, 0xd1, /* Damper Low LSB */ + VS6624_NORA_HIGH_THR_MSB, 0x68, /* Damper High MSB */ + VS6624_NORA_HIGH_THR_LSB, 0xdd, /* Damper High LSB */ + VS6624_NORA_MIN_OUT_MSB, 0x3a, /* Damper Min output MSB */ + VS6624_NORA_MIN_OUT_LSB, 0x00, /* Damper Min output LSB */ + VS6624_F2B_DISABLE, 0x00, /* Disable */ + 0x1d8a, 0x30, /* MAXWeightHigh */ + 0x1d91, 0x62, /* fpDamperLowThresholdHigh MSB */ + 0x1d92, 0x4a, /* fpDamperLowThresholdHigh LSB */ + 0x1d95, 0x65, /* fpDamperHighThresholdHigh MSB */ + 0x1d96, 0x0e, /* fpDamperHighThresholdHigh LSB */ + 0x1da1, 0x3a, /* fpMinimumDamperOutputLow MSB */ + 0x1da2, 0xb8, /* fpMinimumDamperOutputLow LSB */ + 0x1e08, 0x06, /* MAXWeightLow */ + 0x1e0a, 0x0a, /* MAXWeightHigh */ + 0x1601, 0x3a, /* Red A MSB */ + 0x1602, 0x14, /* Red A LSB */ + 0x1605, 0x3b, /* Blue A MSB */ + 0x1606, 0x85, /* BLue A LSB */ + 0x1609, 0x3b, /* RED B MSB */ + 0x160a, 0x85, /* RED B LSB */ + 0x160d, 0x3a, /* Blue B MSB */ + 0x160e, 0x14, /* Blue B LSB */ + 0x1611, 0x30, /* Max Distance from Locus MSB */ + 0x1612, 0x8f, /* Max Distance from Locus MSB */ + 0x1614, 0x01, /* Enable constrainer */ + 0x0000, 0x00, +}; + +static const u16 vs6624_default[] = { + VS6624_CONTRAST0, 0x84, + VS6624_SATURATION0, 0x75, + VS6624_GAMMA0, 0x11, + VS6624_CONTRAST1, 0x84, + VS6624_SATURATION1, 0x75, + VS6624_GAMMA1, 0x11, + VS6624_MAN_RG, 0x80, + VS6624_MAN_GG, 0x80, + VS6624_MAN_BG, 0x80, + VS6624_WB_MODE, 0x1, + VS6624_EXPO_COMPENSATION, 0xfe, + VS6624_EXPO_METER, 0x0, + VS6624_LIGHT_FREQ, 0x64, + VS6624_PEAK_GAIN, 0xe, + VS6624_PEAK_LOW_THR, 0x28, + VS6624_HMIRROR0, 0x0, + VS6624_VFLIP0, 0x0, + VS6624_ZOOM_HSTEP0_MSB, 0x0, + VS6624_ZOOM_HSTEP0_LSB, 0x1, + VS6624_ZOOM_VSTEP0_MSB, 0x0, + VS6624_ZOOM_VSTEP0_LSB, 0x1, + VS6624_PAN_HSTEP0_MSB, 0x0, + VS6624_PAN_HSTEP0_LSB, 0xf, + VS6624_PAN_VSTEP0_MSB, 0x0, + VS6624_PAN_VSTEP0_LSB, 0xf, + VS6624_SENSOR_MODE, 0x1, + VS6624_SYNC_CODE_SETUP, 0x21, + VS6624_DISABLE_FR_DAMPER, 0x0, + VS6624_FR_DEN, 0x1, + VS6624_FR_NUM_LSB, 0xf, + VS6624_INIT_PIPE_SETUP, 0x0, + VS6624_IMG_FMT0, 0x0, + VS6624_YUV_SETUP, 0x1, + VS6624_IMAGE_SIZE0, 0x2, + 0x0000, 0x00, +}; + +static inline struct vs6624 *to_vs6624(struct v4l2_subdev *sd) +{ + return container_of(sd, struct vs6624, sd); +} +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct vs6624, hdl)->sd; +} + +static int vs6624_read(struct v4l2_subdev *sd, u16 index) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 buf[2]; + + buf[0] = index >> 8; + buf[1] = index; + i2c_master_send(client, buf, 2); + i2c_master_recv(client, buf, 1); + + return buf[0]; +} + +static int vs6624_write(struct v4l2_subdev *sd, u16 index, + u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 buf[3]; + + buf[0] = index >> 8; + buf[1] = index; + buf[2] = value; + + return i2c_master_send(client, buf, 3); +} + +static int vs6624_writeregs(struct v4l2_subdev *sd, const u16 *regs) +{ + u16 reg; + u8 data; + + while (*regs != 0x00) { + reg = *regs++; + data = *regs++; + + vs6624_write(sd, reg, data); + } + return 0; +} + +static int vs6624_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + + switch (ctrl->id) { + case V4L2_CID_CONTRAST: + vs6624_write(sd, VS6624_CONTRAST0, ctrl->val); + break; + case V4L2_CID_SATURATION: + vs6624_write(sd, VS6624_SATURATION0, ctrl->val); + break; + case V4L2_CID_HFLIP: + vs6624_write(sd, VS6624_HMIRROR0, ctrl->val); + break; + case V4L2_CID_VFLIP: + vs6624_write(sd, VS6624_VFLIP0, ctrl->val); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int vs6624_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, + enum v4l2_mbus_pixelcode *code) +{ + if (index >= ARRAY_SIZE(vs6624_formats)) + return -EINVAL; + + *code = vs6624_formats[index].mbus_code; + return 0; +} + +static int vs6624_try_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + int index; + + for (index = 0; index < ARRAY_SIZE(vs6624_formats); index++) + if (vs6624_formats[index].mbus_code == fmt->code) + break; + if (index >= ARRAY_SIZE(vs6624_formats)) { + /* default to first format */ + index = 0; + fmt->code = vs6624_formats[0].mbus_code; + } + + /* sensor mode is VGA */ + if (fmt->width > VGA_WIDTH) + fmt->width = VGA_WIDTH; + if (fmt->height > VGA_HEIGHT) + fmt->height = VGA_HEIGHT; + fmt->width = fmt->width & (~3); + fmt->height = fmt->height & (~3); + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = vs6624_formats[index].colorspace; + return 0; +} + +static int vs6624_s_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct vs6624 *sensor = to_vs6624(sd); + int ret; + + ret = vs6624_try_mbus_fmt(sd, fmt); + if (ret) + return ret; + + /* set image format */ + switch (fmt->code) { + case V4L2_MBUS_FMT_UYVY8_2X8: + vs6624_write(sd, VS6624_IMG_FMT0, 0x0); + vs6624_write(sd, VS6624_YUV_SETUP, 0x1); + break; + case V4L2_MBUS_FMT_YUYV8_2X8: + vs6624_write(sd, VS6624_IMG_FMT0, 0x0); + vs6624_write(sd, VS6624_YUV_SETUP, 0x3); + break; + case V4L2_MBUS_FMT_RGB565_2X8_LE: + vs6624_write(sd, VS6624_IMG_FMT0, 0x4); + vs6624_write(sd, VS6624_RGB_SETUP, 0x0); + break; + default: + return -EINVAL; + } + + /* set image size */ + if ((fmt->width == VGA_WIDTH) && (fmt->height == VGA_HEIGHT)) + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x2); + else if ((fmt->width == QVGA_WIDTH) && (fmt->height == QVGA_HEIGHT)) + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x4); + else if ((fmt->width == QQVGA_WIDTH) && (fmt->height == QQVGA_HEIGHT)) + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x6); + else if ((fmt->width == CIF_WIDTH) && (fmt->height == CIF_HEIGHT)) + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x3); + else if ((fmt->width == QCIF_WIDTH) && (fmt->height == QCIF_HEIGHT)) + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x5); + else if ((fmt->width == QQCIF_WIDTH) && (fmt->height == QQCIF_HEIGHT)) + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x7); + else { + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x8); + vs6624_write(sd, VS6624_MAN_HSIZE0_MSB, fmt->width >> 8); + vs6624_write(sd, VS6624_MAN_HSIZE0_LSB, fmt->width & 0xFF); + vs6624_write(sd, VS6624_MAN_VSIZE0_MSB, fmt->height >> 8); + vs6624_write(sd, VS6624_MAN_VSIZE0_LSB, fmt->height & 0xFF); + vs6624_write(sd, VS6624_CROP_CTRL0, 0x1); + } + + sensor->fmt = *fmt; + + return 0; +} + +static int vs6624_g_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct vs6624 *sensor = to_vs6624(sd); + + *fmt = sensor->fmt; + return 0; +} + +static int vs6624_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +{ + struct vs6624 *sensor = to_vs6624(sd); + struct v4l2_captureparm *cp = &parms->parm.capture; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memset(cp, 0, sizeof(*cp)); + cp->capability = V4L2_CAP_TIMEPERFRAME; + cp->timeperframe.numerator = sensor->frame_rate.denominator; + cp->timeperframe.denominator = sensor->frame_rate.numerator; + return 0; +} + +static int vs6624_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +{ + struct vs6624 *sensor = to_vs6624(sd); + struct v4l2_captureparm *cp = &parms->parm.capture; + struct v4l2_fract *tpf = &cp->timeperframe; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (cp->extendedmode != 0) + return -EINVAL; + + if (tpf->numerator == 0 || tpf->denominator == 0 + || (tpf->denominator > tpf->numerator * MAX_FRAME_RATE)) { + /* reset to max frame rate */ + tpf->numerator = 1; + tpf->denominator = MAX_FRAME_RATE; + } + sensor->frame_rate.numerator = tpf->denominator; + sensor->frame_rate.denominator = tpf->numerator; + vs6624_write(sd, VS6624_DISABLE_FR_DAMPER, 0x0); + vs6624_write(sd, VS6624_FR_NUM_MSB, + sensor->frame_rate.numerator >> 8); + vs6624_write(sd, VS6624_FR_NUM_LSB, + sensor->frame_rate.numerator & 0xFF); + vs6624_write(sd, VS6624_FR_DEN, + sensor->frame_rate.denominator & 0xFF); + return 0; +} + +static int vs6624_s_stream(struct v4l2_subdev *sd, int enable) +{ + if (enable) + vs6624_write(sd, VS6624_USER_CMD, 0x2); + else + vs6624_write(sd, VS6624_USER_CMD, 0x4); + udelay(100); + return 0; +} + +static int vs6624_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + int rev; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + rev = (vs6624_read(sd, VS6624_FW_VSN_MAJOR) << 8) + | vs6624_read(sd, VS6624_FW_VSN_MINOR); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VS6624, rev); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vs6624_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = vs6624_read(sd, reg->reg & 0xffff); + reg->size = 1; + return 0; +} + +static int vs6624_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + vs6624_write(sd, reg->reg & 0xffff, reg->val & 0xff); + return 0; +} +#endif + +static const struct v4l2_ctrl_ops vs6624_ctrl_ops = { + .s_ctrl = vs6624_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops vs6624_core_ops = { + .g_chip_ident = vs6624_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = vs6624_g_register, + .s_register = vs6624_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops vs6624_video_ops = { + .enum_mbus_fmt = vs6624_enum_mbus_fmt, + .try_mbus_fmt = vs6624_try_mbus_fmt, + .s_mbus_fmt = vs6624_s_mbus_fmt, + .g_mbus_fmt = vs6624_g_mbus_fmt, + .s_parm = vs6624_s_parm, + .g_parm = vs6624_g_parm, + .s_stream = vs6624_s_stream, +}; + +static const struct v4l2_subdev_ops vs6624_ops = { + .core = &vs6624_core_ops, + .video = &vs6624_video_ops, +}; + +static int __devinit vs6624_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct vs6624 *sensor; + struct v4l2_subdev *sd; + struct v4l2_ctrl_handler *hdl; + const unsigned *ce; + int ret; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -EIO; + + ce = client->dev.platform_data; + if (ce == NULL) + return -EINVAL; + + ret = gpio_request(*ce, "VS6624 Chip Enable"); + if (ret) { + v4l_err(client, "failed to request GPIO %d\n", *ce); + return ret; + } + gpio_direction_output(*ce, 1); + /* wait 100ms before any further i2c writes are performed */ + mdelay(100); + + sensor = kzalloc(sizeof(*sensor), GFP_KERNEL); + if (sensor == NULL) { + gpio_free(*ce); + return -ENOMEM; + } + + sd = &sensor->sd; + v4l2_i2c_subdev_init(sd, client, &vs6624_ops); + + vs6624_writeregs(sd, vs6624_p1); + vs6624_write(sd, VS6624_MICRO_EN, 0x2); + vs6624_write(sd, VS6624_DIO_EN, 0x1); + mdelay(10); + vs6624_writeregs(sd, vs6624_p2); + + vs6624_writeregs(sd, vs6624_default); + vs6624_write(sd, VS6624_HSYNC_SETUP, 0xF); + vs6624_writeregs(sd, vs6624_run_setup); + + /* set frame rate */ + sensor->frame_rate.numerator = MAX_FRAME_RATE; + sensor->frame_rate.denominator = 1; + vs6624_write(sd, VS6624_DISABLE_FR_DAMPER, 0x0); + vs6624_write(sd, VS6624_FR_NUM_MSB, + sensor->frame_rate.numerator >> 8); + vs6624_write(sd, VS6624_FR_NUM_LSB, + sensor->frame_rate.numerator & 0xFF); + vs6624_write(sd, VS6624_FR_DEN, + sensor->frame_rate.denominator & 0xFF); + + sensor->fmt = vs6624_default_fmt; + sensor->ce_pin = *ce; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + hdl = &sensor->hdl; + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, + V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x87); + v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, + V4L2_CID_SATURATION, 0, 0xFF, 1, 0x78); + v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + /* hook the control handler into the driver */ + sd->ctrl_handler = hdl; + if (hdl->error) { + int err = hdl->error; + + v4l2_ctrl_handler_free(hdl); + kfree(sensor); + gpio_free(*ce); + return err; + } + + /* initialize the hardware to the default control values */ + ret = v4l2_ctrl_handler_setup(hdl); + if (ret) { + v4l2_ctrl_handler_free(hdl); + kfree(sensor); + gpio_free(*ce); + } + return ret; +} + +static int __devexit vs6624_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct vs6624 *sensor = to_vs6624(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); + gpio_free(sensor->ce_pin); + kfree(sensor); + return 0; +} + +static const struct i2c_device_id vs6624_id[] = { + {"vs6624", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, vs6624_id); + +static struct i2c_driver vs6624_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "vs6624", + }, + .probe = vs6624_probe, + .remove = __devexit_p(vs6624_remove), + .id_table = vs6624_id, +}; + +static __init int vs6624_init(void) +{ + return i2c_add_driver(&vs6624_driver); +} + +static __exit void vs6624_exit(void) +{ + i2c_del_driver(&vs6624_driver); +} + +module_init(vs6624_init); +module_exit(vs6624_exit); + +MODULE_DESCRIPTION("VS6624 sensor driver"); +MODULE_AUTHOR("Scott Jiang "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/vs6624_regs.h b/drivers/media/i2c/vs6624_regs.h new file mode 100644 index 00000000000..6ba2ee25827 --- /dev/null +++ b/drivers/media/i2c/vs6624_regs.h @@ -0,0 +1,337 @@ +/* + * vs6624 - ST VS6624 CMOS image sensor registers + * + * Copyright (c) 2011 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + */ + +#ifndef _VS6624_REGS_H_ +#define _VS6624_REGS_H_ + +/* low level control registers */ +#define VS6624_MICRO_EN 0xC003 /* power enable for all MCU clock */ +#define VS6624_DIO_EN 0xC044 /* enable digital I/O */ +/* device parameters */ +#define VS6624_DEV_ID_MSB 0x0001 /* device id MSB */ +#define VS6624_DEV_ID_LSB 0x0002 /* device id LSB */ +#define VS6624_FW_VSN_MAJOR 0x0004 /* firmware version major */ +#define VS6624_FW_VSN_MINOR 0x0006 /* firmware version minor */ +#define VS6624_PATCH_VSN_MAJOR 0x0008 /* patch version major */ +#define VS6624_PATCH_VSN_MINOR 0x000A /* patch version minor */ +/* host interface manager control */ +#define VS6624_USER_CMD 0x0180 /* user level control of operating states */ +/* host interface manager status */ +#define VS6624_STATE 0x0202 /* current state of the mode manager */ +/* run mode control */ +#define VS6624_METER_ON 0x0280 /* if false AE and AWB are disabled */ +/* mode setup */ +#define VS6624_ACTIVE_PIPE_SETUP 0x0302 /* select the active bank for non view live mode */ +#define VS6624_SENSOR_MODE 0x0308 /* select the different sensor mode */ +/* pipe setup bank0 */ +#define VS6624_IMAGE_SIZE0 0x0380 /* required output dimension */ +#define VS6624_MAN_HSIZE0_MSB 0x0383 /* input required manual H size MSB */ +#define VS6624_MAN_HSIZE0_LSB 0x0384 /* input required manual H size LSB */ +#define VS6624_MAN_VSIZE0_MSB 0x0387 /* input required manual V size MSB */ +#define VS6624_MAN_VSIZE0_LSB 0x0388 /* input required manual V size LSB */ +#define VS6624_ZOOM_HSTEP0_MSB 0x038B /* set the zoom H step MSB */ +#define VS6624_ZOOM_HSTEP0_LSB 0x038C /* set the zoom H step LSB */ +#define VS6624_ZOOM_VSTEP0_MSB 0x038F /* set the zoom V step MSB */ +#define VS6624_ZOOM_VSTEP0_LSB 0x0390 /* set the zoom V step LSB */ +#define VS6624_ZOOM_CTRL0 0x0392 /* control zoon in, out and stop */ +#define VS6624_PAN_HSTEP0_MSB 0x0395 /* set the pan H step MSB */ +#define VS6624_PAN_HSTEP0_LSB 0x0396 /* set the pan H step LSB */ +#define VS6624_PAN_VSTEP0_MSB 0x0399 /* set the pan V step MSB */ +#define VS6624_PAN_VSTEP0_LSB 0x039A /* set the pan V step LSB */ +#define VS6624_PAN_CTRL0 0x039C /* control pan operation */ +#define VS6624_CROP_CTRL0 0x039E /* select cropping mode */ +#define VS6624_CROP_HSTART0_MSB 0x03A1 /* set the cropping H start address MSB */ +#define VS6624_CROP_HSTART0_LSB 0x03A2 /* set the cropping H start address LSB */ +#define VS6624_CROP_HSIZE0_MSB 0x03A5 /* set the cropping H size MSB */ +#define VS6624_CROP_HSIZE0_LSB 0x03A6 /* set the cropping H size LSB */ +#define VS6624_CROP_VSTART0_MSB 0x03A9 /* set the cropping V start address MSB */ +#define VS6624_CROP_VSTART0_LSB 0x03AA /* set the cropping V start address LSB */ +#define VS6624_CROP_VSIZE0_MSB 0x03AD /* set the cropping V size MSB */ +#define VS6624_CROP_VSIZE0_LSB 0x03AE /* set the cropping V size LSB */ +#define VS6624_IMG_FMT0 0x03B0 /* select required output image format */ +#define VS6624_BAYER_OUT_ALIGN0 0x03B2 /* set bayer output alignment */ +#define VS6624_CONTRAST0 0x03B4 /* contrast control for output */ +#define VS6624_SATURATION0 0x03B6 /* saturation control for output */ +#define VS6624_GAMMA0 0x03B8 /* gamma settings */ +#define VS6624_HMIRROR0 0x03BA /* horizontal image orientation flip */ +#define VS6624_VFLIP0 0x03BC /* vertical image orientation flip */ +#define VS6624_CHANNEL_ID0 0x03BE /* logical DMA channel number */ +/* pipe setup bank1 */ +#define VS6624_IMAGE_SIZE1 0x0400 /* required output dimension */ +#define VS6624_MAN_HSIZE1_MSB 0x0403 /* input required manual H size MSB */ +#define VS6624_MAN_HSIZE1_LSB 0x0404 /* input required manual H size LSB */ +#define VS6624_MAN_VSIZE1_MSB 0x0407 /* input required manual V size MSB */ +#define VS6624_MAN_VSIZE1_LSB 0x0408 /* input required manual V size LSB */ +#define VS6624_ZOOM_HSTEP1_MSB 0x040B /* set the zoom H step MSB */ +#define VS6624_ZOOM_HSTEP1_LSB 0x040C /* set the zoom H step LSB */ +#define VS6624_ZOOM_VSTEP1_MSB 0x040F /* set the zoom V step MSB */ +#define VS6624_ZOOM_VSTEP1_LSB 0x0410 /* set the zoom V step LSB */ +#define VS6624_ZOOM_CTRL1 0x0412 /* control zoon in, out and stop */ +#define VS6624_PAN_HSTEP1_MSB 0x0415 /* set the pan H step MSB */ +#define VS6624_PAN_HSTEP1_LSB 0x0416 /* set the pan H step LSB */ +#define VS6624_PAN_VSTEP1_MSB 0x0419 /* set the pan V step MSB */ +#define VS6624_PAN_VSTEP1_LSB 0x041A /* set the pan V step LSB */ +#define VS6624_PAN_CTRL1 0x041C /* control pan operation */ +#define VS6624_CROP_CTRL1 0x041E /* select cropping mode */ +#define VS6624_CROP_HSTART1_MSB 0x0421 /* set the cropping H start address MSB */ +#define VS6624_CROP_HSTART1_LSB 0x0422 /* set the cropping H start address LSB */ +#define VS6624_CROP_HSIZE1_MSB 0x0425 /* set the cropping H size MSB */ +#define VS6624_CROP_HSIZE1_LSB 0x0426 /* set the cropping H size LSB */ +#define VS6624_CROP_VSTART1_MSB 0x0429 /* set the cropping V start address MSB */ +#define VS6624_CROP_VSTART1_LSB 0x042A /* set the cropping V start address LSB */ +#define VS6624_CROP_VSIZE1_MSB 0x042D /* set the cropping V size MSB */ +#define VS6624_CROP_VSIZE1_LSB 0x042E /* set the cropping V size LSB */ +#define VS6624_IMG_FMT1 0x0430 /* select required output image format */ +#define VS6624_BAYER_OUT_ALIGN1 0x0432 /* set bayer output alignment */ +#define VS6624_CONTRAST1 0x0434 /* contrast control for output */ +#define VS6624_SATURATION1 0x0436 /* saturation control for output */ +#define VS6624_GAMMA1 0x0438 /* gamma settings */ +#define VS6624_HMIRROR1 0x043A /* horizontal image orientation flip */ +#define VS6624_VFLIP1 0x043C /* vertical image orientation flip */ +#define VS6624_CHANNEL_ID1 0x043E /* logical DMA channel number */ +/* view live control */ +#define VS6624_VIEW_LIVE_EN 0x0480 /* enable view live mode */ +#define VS6624_INIT_PIPE_SETUP 0x0482 /* select initial pipe setup bank */ +/* view live status */ +#define VS6624_CUR_PIPE_SETUP 0x0500 /* indicates most recently applied setup bank */ +/* power management */ +#define VS6624_TIME_TO_POWER_DOWN 0x0580 /* automatically transition time to stop mode */ +/* video timing parameter host inputs */ +#define VS6624_EXT_CLK_FREQ_NUM_MSB 0x0605 /* external clock frequency numerator MSB */ +#define VS6624_EXT_CLK_FREQ_NUM_LSB 0x0606 /* external clock frequency numerator LSB */ +#define VS6624_EXT_CLK_FREQ_DEN 0x0608 /* external clock frequency denominator */ +/* video timing control */ +#define VS6624_SYS_CLK_MODE 0x0880 /* decides system clock frequency */ +/* frame dimension parameter host inputs */ +#define VS6624_LIGHT_FREQ 0x0C80 /* AC frequency used for flicker free time */ +#define VS6624_FLICKER_COMPAT 0x0C82 /* flicker compatible frame length */ +/* static frame rate control */ +#define VS6624_FR_NUM_MSB 0x0D81 /* desired frame rate numerator MSB */ +#define VS6624_FR_NUM_LSB 0x0D82 /* desired frame rate numerator LSB */ +#define VS6624_FR_DEN 0x0D84 /* desired frame rate denominator */ +/* automatic frame rate control */ +#define VS6624_DISABLE_FR_DAMPER 0x0E80 /* defines frame rate mode */ +#define VS6624_MIN_DAMPER_OUT_MSB 0x0E8C /* minimum frame rate MSB */ +#define VS6624_MIN_DAMPER_OUT_LSB 0x0E8A /* minimum frame rate LSB */ +/* exposure controls */ +#define VS6624_EXPO_MODE 0x1180 /* exposure mode */ +#define VS6624_EXPO_METER 0x1182 /* weights to be associated with the zones */ +#define VS6624_EXPO_TIME_NUM 0x1184 /* exposure time numerator */ +#define VS6624_EXPO_TIME_DEN 0x1186 /* exposure time denominator */ +#define VS6624_EXPO_TIME_MSB 0x1189 /* exposure time for the Manual Mode MSB */ +#define VS6624_EXPO_TIME_LSB 0x118A /* exposure time for the Manual Mode LSB */ +#define VS6624_EXPO_COMPENSATION 0x1190 /* exposure compensation */ +#define VS6624_DIRECT_COARSE_MSB 0x1195 /* coarse integration lines for Direct Mode MSB */ +#define VS6624_DIRECT_COARSE_LSB 0x1196 /* coarse integration lines for Direct Mode LSB */ +#define VS6624_DIRECT_FINE_MSB 0x1199 /* fine integration pixels for Direct Mode MSB */ +#define VS6624_DIRECT_FINE_LSB 0x119A /* fine integration pixels for Direct Mode LSB */ +#define VS6624_DIRECT_ANAL_GAIN_MSB 0x119D /* analog gain for Direct Mode MSB */ +#define VS6624_DIRECT_ANAL_GAIN_LSB 0x119E /* analog gain for Direct Mode LSB */ +#define VS6624_DIRECT_DIGI_GAIN_MSB 0x11A1 /* digital gain for Direct Mode MSB */ +#define VS6624_DIRECT_DIGI_GAIN_LSB 0x11A2 /* digital gain for Direct Mode LSB */ +#define VS6624_FLASH_COARSE_MSB 0x11A5 /* coarse integration lines for Flash Gun Mode MSB */ +#define VS6624_FLASH_COARSE_LSB 0x11A6 /* coarse integration lines for Flash Gun Mode LSB */ +#define VS6624_FLASH_FINE_MSB 0x11A9 /* fine integration pixels for Flash Gun Mode MSB */ +#define VS6624_FLASH_FINE_LSB 0x11AA /* fine integration pixels for Flash Gun Mode LSB */ +#define VS6624_FLASH_ANAL_GAIN_MSB 0x11AD /* analog gain for Flash Gun Mode MSB */ +#define VS6624_FLASH_ANAL_GAIN_LSB 0x11AE /* analog gain for Flash Gun Mode LSB */ +#define VS6624_FLASH_DIGI_GAIN_MSB 0x11B1 /* digital gain for Flash Gun Mode MSB */ +#define VS6624_FLASH_DIGI_GAIN_LSB 0x11B2 /* digital gain for Flash Gun Mode LSB */ +#define VS6624_FREEZE_AE 0x11B4 /* freeze auto exposure */ +#define VS6624_MAX_INT_TIME_MSB 0x11B7 /* user maximum integration time MSB */ +#define VS6624_MAX_INT_TIME_LSB 0x11B8 /* user maximum integration time LSB */ +#define VS6624_FLASH_AG_THR_MSB 0x11BB /* recommend flash gun analog gain threshold MSB */ +#define VS6624_FLASH_AG_THR_LSB 0x11BC /* recommend flash gun analog gain threshold LSB */ +#define VS6624_ANTI_FLICKER_MODE 0x11C0 /* anti flicker mode */ +/* white balance control */ +#define VS6624_WB_MODE 0x1480 /* set white balance mode */ +#define VS6624_MAN_RG 0x1482 /* user setting for red channel gain */ +#define VS6624_MAN_GG 0x1484 /* user setting for green channel gain */ +#define VS6624_MAN_BG 0x1486 /* user setting for blue channel gain */ +#define VS6624_FLASH_RG_MSB 0x148B /* red gain for Flash Gun MSB */ +#define VS6624_FLASH_RG_LSB 0x148C /* red gain for Flash Gun LSB */ +#define VS6624_FLASH_GG_MSB 0x148F /* green gain for Flash Gun MSB */ +#define VS6624_FLASH_GG_LSB 0x1490 /* green gain for Flash Gun LSB */ +#define VS6624_FLASH_BG_MSB 0x1493 /* blue gain for Flash Gun MSB */ +#define VS6624_FLASH_BG_LSB 0x1494 /* blue gain for Flash Gun LSB */ +/* sensor setup */ +#define VS6624_BC_OFFSET 0x1990 /* Black Correction Offset */ +/* image stability */ +#define VS6624_STABLE_WB 0x1900 /* white balance stable */ +#define VS6624_STABLE_EXPO 0x1902 /* exposure stable */ +#define VS6624_STABLE 0x1906 /* system stable */ +/* flash control */ +#define VS6624_FLASH_MODE 0x1A80 /* flash mode */ +#define VS6624_FLASH_OFF_LINE_MSB 0x1A83 /* off line at flash pulse mode MSB */ +#define VS6624_FLASH_OFF_LINE_LSB 0x1A84 /* off line at flash pulse mode LSB */ +/* flash status */ +#define VS6624_FLASH_RECOM 0x1B00 /* flash gun is recommended */ +#define VS6624_FLASH_GRAB_COMPLETE 0x1B02 /* flash gun image has been grabbed */ +/* scythe filter controls */ +#define VS6624_SCYTHE_FILTER 0x1D80 /* disable scythe defect correction */ +/* jack filter controls */ +#define VS6624_JACK_FILTER 0x1E00 /* disable jack defect correction */ +/* demosaic control */ +#define VS6624_ANTI_ALIAS_FILTER 0x1E80 /* anti alias filter suppress */ +/* color matrix dampers */ +#define VS6624_CM_DISABLE 0x1F00 /* disable color matrix damper */ +#define VS6624_CM_LOW_THR_MSB 0x1F03 /* low threshold for exposure MSB */ +#define VS6624_CM_LOW_THR_LSB 0x1F04 /* low threshold for exposure LSB */ +#define VS6624_CM_HIGH_THR_MSB 0x1F07 /* high threshold for exposure MSB */ +#define VS6624_CM_HIGH_THR_LSB 0x1F08 /* high threshold for exposure LSB */ +#define VS6624_CM_MIN_OUT_MSB 0x1F0B /* minimum possible damper output MSB */ +#define VS6624_CM_MIN_OUT_LSB 0x1F0C /* minimum possible damper output LSB */ +/* peaking control */ +#define VS6624_PEAK_GAIN 0x2000 /* controls peaking gain */ +#define VS6624_PEAK_G_DISABLE 0x2002 /* disable peak gain damping */ +#define VS6624_PEAK_LOW_THR_G_MSB 0x2005 /* low threshold for exposure for gain MSB */ +#define VS6624_PEAK_LOW_THR_G_LSB 0x2006 /* low threshold for exposure for gain LSB */ +#define VS6624_PEAK_HIGH_THR_G_MSB 0x2009 /* high threshold for exposure for gain MSB */ +#define VS6624_PEAK_HIGH_THR_G_LSB 0x200A /* high threshold for exposure for gain LSB */ +#define VS6624_PEAK_MIN_OUT_G_MSB 0x200D /* minimum damper output for gain MSB */ +#define VS6624_PEAK_MIN_OUT_G_LSB 0x200E /* minimum damper output for gain LSB */ +#define VS6624_PEAK_LOW_THR 0x2010 /* adjust degree of coring */ +#define VS6624_PEAK_C_DISABLE 0x2012 /* disable coring damping */ +#define VS6624_PEAK_HIGH_THR 0x2014 /* adjust maximum gain */ +#define VS6624_PEAK_LOW_THR_C_MSB 0x2017 /* low threshold for exposure for coring MSB */ +#define VS6624_PEAK_LOW_THR_C_LSB 0x2018 /* low threshold for exposure for coring LSB */ +#define VS6624_PEAK_HIGH_THR_C_MSB 0x201B /* high threshold for exposure for coring MSB */ +#define VS6624_PEAK_HIGH_THR_C_LSB 0x201C /* high threshold for exposure for coring LSB */ +#define VS6624_PEAK_MIN_OUT_C_MSB 0x201F /* minimum damper output for coring MSB */ +#define VS6624_PEAK_MIN_OUT_C_LSB 0x2020 /* minimum damper output for coring LSB */ +/* pipe 0 RGB to YUV matrix manual control */ +#define VS6624_RYM0_MAN_CTRL 0x2180 /* enable manual RGB to YUV matrix */ +#define VS6624_RYM0_W00_MSB 0x2183 /* row 0 column 0 of YUV matrix MSB */ +#define VS6624_RYM0_W00_LSB 0x2184 /* row 0 column 0 of YUV matrix LSB */ +#define VS6624_RYM0_W01_MSB 0x2187 /* row 0 column 1 of YUV matrix MSB */ +#define VS6624_RYM0_W01_LSB 0x2188 /* row 0 column 1 of YUV matrix LSB */ +#define VS6624_RYM0_W02_MSB 0x218C /* row 0 column 2 of YUV matrix MSB */ +#define VS6624_RYM0_W02_LSB 0x218D /* row 0 column 2 of YUV matrix LSB */ +#define VS6624_RYM0_W10_MSB 0x2190 /* row 1 column 0 of YUV matrix MSB */ +#define VS6624_RYM0_W10_LSB 0x218F /* row 1 column 0 of YUV matrix LSB */ +#define VS6624_RYM0_W11_MSB 0x2193 /* row 1 column 1 of YUV matrix MSB */ +#define VS6624_RYM0_W11_LSB 0x2194 /* row 1 column 1 of YUV matrix LSB */ +#define VS6624_RYM0_W12_MSB 0x2197 /* row 1 column 2 of YUV matrix MSB */ +#define VS6624_RYM0_W12_LSB 0x2198 /* row 1 column 2 of YUV matrix LSB */ +#define VS6624_RYM0_W20_MSB 0x219B /* row 2 column 0 of YUV matrix MSB */ +#define VS6624_RYM0_W20_LSB 0x219C /* row 2 column 0 of YUV matrix LSB */ +#define VS6624_RYM0_W21_MSB 0x21A0 /* row 2 column 1 of YUV matrix MSB */ +#define VS6624_RYM0_W21_LSB 0x219F /* row 2 column 1 of YUV matrix LSB */ +#define VS6624_RYM0_W22_MSB 0x21A3 /* row 2 column 2 of YUV matrix MSB */ +#define VS6624_RYM0_W22_LSB 0x21A4 /* row 2 column 2 of YUV matrix LSB */ +#define VS6624_RYM0_YINY_MSB 0x21A7 /* Y in Y MSB */ +#define VS6624_RYM0_YINY_LSB 0x21A8 /* Y in Y LSB */ +#define VS6624_RYM0_YINCB_MSB 0x21AB /* Y in Cb MSB */ +#define VS6624_RYM0_YINCB_LSB 0x21AC /* Y in Cb LSB */ +#define VS6624_RYM0_YINCR_MSB 0x21B0 /* Y in Cr MSB */ +#define VS6624_RYM0_YINCR_LSB 0x21AF /* Y in Cr LSB */ +/* pipe 1 RGB to YUV matrix manual control */ +#define VS6624_RYM1_MAN_CTRL 0x2200 /* enable manual RGB to YUV matrix */ +#define VS6624_RYM1_W00_MSB 0x2203 /* row 0 column 0 of YUV matrix MSB */ +#define VS6624_RYM1_W00_LSB 0x2204 /* row 0 column 0 of YUV matrix LSB */ +#define VS6624_RYM1_W01_MSB 0x2207 /* row 0 column 1 of YUV matrix MSB */ +#define VS6624_RYM1_W01_LSB 0x2208 /* row 0 column 1 of YUV matrix LSB */ +#define VS6624_RYM1_W02_MSB 0x220C /* row 0 column 2 of YUV matrix MSB */ +#define VS6624_RYM1_W02_LSB 0x220D /* row 0 column 2 of YUV matrix LSB */ +#define VS6624_RYM1_W10_MSB 0x2210 /* row 1 column 0 of YUV matrix MSB */ +#define VS6624_RYM1_W10_LSB 0x220F /* row 1 column 0 of YUV matrix LSB */ +#define VS6624_RYM1_W11_MSB 0x2213 /* row 1 column 1 of YUV matrix MSB */ +#define VS6624_RYM1_W11_LSB 0x2214 /* row 1 column 1 of YUV matrix LSB */ +#define VS6624_RYM1_W12_MSB 0x2217 /* row 1 column 2 of YUV matrix MSB */ +#define VS6624_RYM1_W12_LSB 0x2218 /* row 1 column 2 of YUV matrix LSB */ +#define VS6624_RYM1_W20_MSB 0x221B /* row 2 column 0 of YUV matrix MSB */ +#define VS6624_RYM1_W20_LSB 0x221C /* row 2 column 0 of YUV matrix LSB */ +#define VS6624_RYM1_W21_MSB 0x2220 /* row 2 column 1 of YUV matrix MSB */ +#define VS6624_RYM1_W21_LSB 0x221F /* row 2 column 1 of YUV matrix LSB */ +#define VS6624_RYM1_W22_MSB 0x2223 /* row 2 column 2 of YUV matrix MSB */ +#define VS6624_RYM1_W22_LSB 0x2224 /* row 2 column 2 of YUV matrix LSB */ +#define VS6624_RYM1_YINY_MSB 0x2227 /* Y in Y MSB */ +#define VS6624_RYM1_YINY_LSB 0x2228 /* Y in Y LSB */ +#define VS6624_RYM1_YINCB_MSB 0x222B /* Y in Cb MSB */ +#define VS6624_RYM1_YINCB_LSB 0x222C /* Y in Cb LSB */ +#define VS6624_RYM1_YINCR_MSB 0x2220 /* Y in Cr MSB */ +#define VS6624_RYM1_YINCR_LSB 0x222F /* Y in Cr LSB */ +/* pipe 0 gamma manual control */ +#define VS6624_GAMMA_MAN_CTRL0 0x2280 /* enable manual gamma setup */ +#define VS6624_GAMMA_PEAK_R0 0x2282 /* peaked red channel gamma value */ +#define VS6624_GAMMA_PEAK_G0 0x2284 /* peaked green channel gamma value */ +#define VS6624_GAMMA_PEAK_B0 0x2286 /* peaked blue channel gamma value */ +#define VS6624_GAMMA_UNPEAK_R0 0x2288 /* unpeaked red channel gamma value */ +#define VS6624_GAMMA_UNPEAK_G0 0x228A /* unpeaked green channel gamma value */ +#define VS6624_GAMMA_UNPEAK_B0 0x228C /* unpeaked blue channel gamma value */ +/* pipe 1 gamma manual control */ +#define VS6624_GAMMA_MAN_CTRL1 0x2300 /* enable manual gamma setup */ +#define VS6624_GAMMA_PEAK_R1 0x2302 /* peaked red channel gamma value */ +#define VS6624_GAMMA_PEAK_G1 0x2304 /* peaked green channel gamma value */ +#define VS6624_GAMMA_PEAK_B1 0x2306 /* peaked blue channel gamma value */ +#define VS6624_GAMMA_UNPEAK_R1 0x2308 /* unpeaked red channel gamma value */ +#define VS6624_GAMMA_UNPEAK_G1 0x230A /* unpeaked green channel gamma value */ +#define VS6624_GAMMA_UNPEAK_B1 0x230C /* unpeaked blue channel gamma value */ +/* fade to black */ +#define VS6624_F2B_DISABLE 0x2480 /* disable fade to black */ +#define VS6624_F2B_BLACK_VAL_MSB 0x2483 /* black value MSB */ +#define VS6624_F2B_BLACK_VAL_LSB 0x2484 /* black value LSB */ +#define VS6624_F2B_LOW_THR_MSB 0x2487 /* low threshold for exposure MSB */ +#define VS6624_F2B_LOW_THR_LSB 0x2488 /* low threshold for exposure LSB */ +#define VS6624_F2B_HIGH_THR_MSB 0x248B /* high threshold for exposure MSB */ +#define VS6624_F2B_HIGH_THR_LSB 0x248C /* high threshold for exposure LSB */ +#define VS6624_F2B_MIN_OUT_MSB 0x248F /* minimum damper output MSB */ +#define VS6624_F2B_MIN_OUT_LSB 0x2490 /* minimum damper output LSB */ +/* output formatter control */ +#define VS6624_CODE_CK_EN 0x2580 /* code check enable */ +#define VS6624_BLANK_FMT 0x2582 /* blank format */ +#define VS6624_SYNC_CODE_SETUP 0x2584 /* sync code setup */ +#define VS6624_HSYNC_SETUP 0x2586 /* H sync setup */ +#define VS6624_VSYNC_SETUP 0x2588 /* V sync setup */ +#define VS6624_PCLK_SETUP 0x258A /* PCLK setup */ +#define VS6624_PCLK_EN 0x258C /* PCLK enable */ +#define VS6624_OPF_SP_SETUP 0x258E /* output formatter sp setup */ +#define VS6624_BLANK_DATA_MSB 0x2590 /* blank data MSB */ +#define VS6624_BLANK_DATA_LSB 0x2592 /* blank data LSB */ +#define VS6624_RGB_SETUP 0x2594 /* RGB setup */ +#define VS6624_YUV_SETUP 0x2596 /* YUV setup */ +#define VS6624_VSYNC_RIS_COARSE_H 0x2598 /* V sync rising coarse high */ +#define VS6624_VSYNC_RIS_COARSE_L 0x259A /* V sync rising coarse low */ +#define VS6624_VSYNC_RIS_FINE_H 0x259C /* V sync rising fine high */ +#define VS6624_VSYNC_RIS_FINE_L 0x259E /* V sync rising fine low */ +#define VS6624_VSYNC_FALL_COARSE_H 0x25A0 /* V sync falling coarse high */ +#define VS6624_VSYNC_FALL_COARSE_L 0x25A2 /* V sync falling coarse low */ +#define VS6624_VSYNC_FALL_FINE_H 0x25A4 /* V sync falling fine high */ +#define VS6624_VSYNC_FALL_FINE_L 0x25A6 /* V sync falling fine low */ +#define VS6624_HSYNC_RIS_H 0x25A8 /* H sync rising high */ +#define VS6624_HSYNC_RIS_L 0x25AA /* H sync rising low */ +#define VS6624_HSYNC_FALL_H 0x25AC /* H sync falling high */ +#define VS6624_HSYNC_FALL_L 0x25AE /* H sync falling low */ +#define VS6624_OUT_IF 0x25B0 /* output interface */ +#define VS6624_CCP_EXT_DATA 0x25B2 /* CCP extra data */ +/* NoRA controls */ +#define VS6624_NORA_DISABLE 0x2600 /* NoRA control mode */ +#define VS6624_NORA_USAGE 0x2602 /* usage */ +#define VS6624_NORA_SPLIT_KN 0x2604 /* split kn */ +#define VS6624_NORA_SPLIT_NI 0x2606 /* split ni */ +#define VS6624_NORA_TIGHT_G 0x2608 /* tight green */ +#define VS6624_NORA_DISABLE_NP 0x260A /* disable noro promoting */ +#define VS6624_NORA_LOW_THR_MSB 0x260D /* low threshold for exposure MSB */ +#define VS6624_NORA_LOW_THR_LSB 0x260E /* low threshold for exposure LSB */ +#define VS6624_NORA_HIGH_THR_MSB 0x2611 /* high threshold for exposure MSB */ +#define VS6624_NORA_HIGH_THR_LSB 0x2612 /* high threshold for exposure LSB */ +#define VS6624_NORA_MIN_OUT_MSB 0x2615 /* minimum damper output MSB */ +#define VS6624_NORA_MIN_OUT_LSB 0x2616 /* minimum damper output LSB */ + +#endif diff --git a/drivers/media/i2c/wm8739.c b/drivers/media/i2c/wm8739.c new file mode 100644 index 00000000000..3bb99e93feb --- /dev/null +++ b/drivers/media/i2c/wm8739.c @@ -0,0 +1,294 @@ +/* + * wm8739 + * + * Copyright (C) 2005 T. Adachi + * + * Copyright (C) 2005 Hans Verkuil + * - Cleanup + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("wm8739 driver"); +MODULE_AUTHOR("T. Adachi, Hans Verkuil"); +MODULE_LICENSE("GPL"); + +static int debug; + +module_param(debug, int, 0644); + +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + + +/* ------------------------------------------------------------------------ */ + +enum { + R0 = 0, R1, + R5 = 5, R6, R7, R8, R9, R15 = 15, + TOT_REGS +}; + +struct wm8739_state { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + struct { + /* audio cluster */ + struct v4l2_ctrl *volume; + struct v4l2_ctrl *mute; + struct v4l2_ctrl *balance; + }; + u32 clock_freq; +}; + +static inline struct wm8739_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct wm8739_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct wm8739_state, hdl)->sd; +} + +/* ------------------------------------------------------------------------ */ + +static int wm8739_write(struct v4l2_subdev *sd, int reg, u16 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int i; + + if (reg < 0 || reg >= TOT_REGS) { + v4l2_err(sd, "Invalid register R%d\n", reg); + return -1; + } + + v4l2_dbg(1, debug, sd, "write: %02x %02x\n", reg, val); + + for (i = 0; i < 3; i++) + if (i2c_smbus_write_byte_data(client, + (reg << 1) | (val >> 8), val & 0xff) == 0) + return 0; + v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg); + return -1; +} + +static int wm8739_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct wm8739_state *state = to_state(sd); + unsigned int work_l, work_r; + u8 vol_l; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ + u8 vol_r; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ + u16 mute; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + break; + + default: + return -EINVAL; + } + + /* normalize ( 65535 to 0 -> 31 to 0 (12dB to -34.5dB) ) */ + work_l = (min(65536 - state->balance->val, 32768) * state->volume->val) / 32768; + work_r = (min(state->balance->val, 32768) * state->volume->val) / 32768; + + vol_l = (long)work_l * 31 / 65535; + vol_r = (long)work_r * 31 / 65535; + + /* set audio volume etc. */ + mute = state->mute->val ? 0x80 : 0; + + /* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB + * Default setting: 0x17 = 0 dB + */ + wm8739_write(sd, R0, (vol_l & 0x1f) | mute); + wm8739_write(sd, R1, (vol_r & 0x1f) | mute); + return 0; +} + +/* ------------------------------------------------------------------------ */ + +static int wm8739_s_clock_freq(struct v4l2_subdev *sd, u32 audiofreq) +{ + struct wm8739_state *state = to_state(sd); + + state->clock_freq = audiofreq; + /* de-activate */ + wm8739_write(sd, R9, 0x000); + switch (audiofreq) { + case 44100: + /* 256fps, fs=44.1k */ + wm8739_write(sd, R8, 0x020); + break; + case 48000: + /* 256fps, fs=48k */ + wm8739_write(sd, R8, 0x000); + break; + case 32000: + /* 256fps, fs=32k */ + wm8739_write(sd, R8, 0x018); + break; + default: + break; + } + /* activate */ + wm8739_write(sd, R9, 0x001); + return 0; +} + +static int wm8739_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8739, 0); +} + +static int wm8739_log_status(struct v4l2_subdev *sd) +{ + struct wm8739_state *state = to_state(sd); + + v4l2_info(sd, "Frequency: %u Hz\n", state->clock_freq); + v4l2_ctrl_handler_log_status(&state->hdl, sd->name); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops wm8739_ctrl_ops = { + .s_ctrl = wm8739_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops wm8739_core_ops = { + .log_status = wm8739_log_status, + .g_chip_ident = wm8739_g_chip_ident, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, +}; + +static const struct v4l2_subdev_audio_ops wm8739_audio_ops = { + .s_clock_freq = wm8739_s_clock_freq, +}; + +static const struct v4l2_subdev_ops wm8739_ops = { + .core = &wm8739_core_ops, + .audio = &wm8739_audio_ops, +}; + +/* ------------------------------------------------------------------------ */ + +/* i2c implementation */ + +static int wm8739_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct wm8739_state *state; + struct v4l2_subdev *sd; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct wm8739_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &wm8739_ops); + v4l2_ctrl_handler_init(&state->hdl, 2); + state->volume = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 50736); + state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + state->balance = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops, + V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768); + sd->ctrl_handler = &state->hdl; + if (state->hdl.error) { + int err = state->hdl.error; + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + v4l2_ctrl_cluster(3, &state->volume); + + state->clock_freq = 48000; + + /* Initialize wm8739 */ + + /* reset */ + wm8739_write(sd, R15, 0x00); + /* filter setting, high path, offet clear */ + wm8739_write(sd, R5, 0x000); + /* ADC, OSC, Power Off mode Disable */ + wm8739_write(sd, R6, 0x000); + /* Digital Audio interface format: + Enable Master mode, 24 bit, MSB first/left justified */ + wm8739_write(sd, R7, 0x049); + /* sampling control: normal, 256fs, 48KHz sampling rate */ + wm8739_write(sd, R8, 0x000); + /* activate */ + wm8739_write(sd, R9, 0x001); + /* set volume/mute */ + v4l2_ctrl_handler_setup(&state->hdl); + return 0; +} + +static int wm8739_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct wm8739_state *state = to_state(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&state->hdl); + kfree(to_state(sd)); + return 0; +} + +static const struct i2c_device_id wm8739_id[] = { + { "wm8739", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8739_id); + +static struct i2c_driver wm8739_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "wm8739", + }, + .probe = wm8739_probe, + .remove = wm8739_remove, + .id_table = wm8739_id, +}; + +module_i2c_driver(wm8739_driver); diff --git a/drivers/media/i2c/wm8775.c b/drivers/media/i2c/wm8775.c new file mode 100644 index 00000000000..bee77ea9f49 --- /dev/null +++ b/drivers/media/i2c/wm8775.c @@ -0,0 +1,342 @@ +/* + * wm8775 - driver version 0.0.1 + * + * Copyright (C) 2004 Ulf Eklund + * + * Based on saa7115 driver + * + * Copyright (C) 2005 Hans Verkuil + * - Cleanup + * - V4L2 API update + * - sound fixes + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("wm8775 driver"); +MODULE_AUTHOR("Ulf Eklund, Hans Verkuil"); +MODULE_LICENSE("GPL"); + + + +/* ----------------------------------------------------------------------- */ + +enum { + R7 = 7, R11 = 11, + R12, R13, R14, R15, R16, R17, R18, R19, R20, R21, R23 = 23, + TOT_REGS +}; + +#define ALC_HOLD 0x85 /* R17: use zero cross detection, ALC hold time 42.6 ms */ +#define ALC_EN 0x100 /* R17: ALC enable */ + +struct wm8775_state { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *mute; + struct v4l2_ctrl *vol; + struct v4l2_ctrl *bal; + struct v4l2_ctrl *loud; + u8 input; /* Last selected input (0-0xf) */ +}; + +static inline struct wm8775_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct wm8775_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct wm8775_state, hdl)->sd; +} + +static int wm8775_write(struct v4l2_subdev *sd, int reg, u16 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int i; + + if (reg < 0 || reg >= TOT_REGS) { + v4l2_err(sd, "Invalid register R%d\n", reg); + return -1; + } + + for (i = 0; i < 3; i++) + if (i2c_smbus_write_byte_data(client, + (reg << 1) | (val >> 8), val & 0xff) == 0) + return 0; + v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg); + return -1; +} + +static void wm8775_set_audio(struct v4l2_subdev *sd, int quietly) +{ + struct wm8775_state *state = to_state(sd); + u8 vol_l, vol_r; + int muted = 0 != state->mute->val; + u16 volume = (u16)state->vol->val; + u16 balance = (u16)state->bal->val; + + /* normalize ( 65535 to 0 -> 255 to 0 (+24dB to -103dB) ) */ + vol_l = (min(65536 - balance, 32768) * volume) >> 23; + vol_r = (min(balance, (u16)32768) * volume) >> 23; + + /* Mute */ + if (muted || quietly) + wm8775_write(sd, R21, 0x0c0 | state->input); + + wm8775_write(sd, R14, vol_l | 0x100); /* 0x100= Left channel ADC zero cross enable */ + wm8775_write(sd, R15, vol_r | 0x100); /* 0x100= Right channel ADC zero cross enable */ + + /* Un-mute */ + if (!muted) + wm8775_write(sd, R21, state->input); +} + +static int wm8775_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct wm8775_state *state = to_state(sd); + + /* There are 4 inputs and one output. Zero or more inputs + are multiplexed together to the output. Hence there are + 16 combinations. + If only one input is active (the normal case) then the + input values 1, 2, 4 or 8 should be used. */ + if (input > 15) { + v4l2_err(sd, "Invalid input %d.\n", input); + return -EINVAL; + } + state->input = input; + if (!v4l2_ctrl_g_ctrl(state->mute)) + return 0; + if (!v4l2_ctrl_g_ctrl(state->vol)) + return 0; + if (!v4l2_ctrl_g_ctrl(state->bal)) + return 0; + wm8775_set_audio(sd, 1); + return 0; +} + +static int wm8775_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_BALANCE: + wm8775_set_audio(sd, 0); + return 0; + case V4L2_CID_AUDIO_LOUDNESS: + wm8775_write(sd, R17, (ctrl->val ? ALC_EN : 0) | ALC_HOLD); + return 0; + } + return -EINVAL; +} + +static int wm8775_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8775, 0); +} + +static int wm8775_log_status(struct v4l2_subdev *sd) +{ + struct wm8775_state *state = to_state(sd); + + v4l2_info(sd, "Input: %d\n", state->input); + v4l2_ctrl_handler_log_status(&state->hdl, sd->name); + return 0; +} + +static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +{ + wm8775_set_audio(sd, 0); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops wm8775_ctrl_ops = { + .s_ctrl = wm8775_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops wm8775_core_ops = { + .log_status = wm8775_log_status, + .g_chip_ident = wm8775_g_chip_ident, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, +}; + +static const struct v4l2_subdev_tuner_ops wm8775_tuner_ops = { + .s_frequency = wm8775_s_frequency, +}; + +static const struct v4l2_subdev_audio_ops wm8775_audio_ops = { + .s_routing = wm8775_s_routing, +}; + +static const struct v4l2_subdev_ops wm8775_ops = { + .core = &wm8775_core_ops, + .tuner = &wm8775_tuner_ops, + .audio = &wm8775_audio_ops, +}; + +/* ----------------------------------------------------------------------- */ + +/* i2c implementation */ + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ + +static int wm8775_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct wm8775_state *state; + struct v4l2_subdev *sd; + int err; + bool is_nova_s = false; + + if (client->dev.platform_data) { + struct wm8775_platform_data *data = client->dev.platform_data; + is_nova_s = data->is_nova_s; + } + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct wm8775_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &wm8775_ops); + state->input = 2; + + v4l2_ctrl_handler_init(&state->hdl, 4); + state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + state->vol = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, 0, 65535, (65535+99)/100, 0xCF00); /* 0dB*/ + state->bal = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, + V4L2_CID_AUDIO_BALANCE, 0, 65535, (65535+99)/100, 32768); + state->loud = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, + V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 1); + sd->ctrl_handler = &state->hdl; + err = state->hdl.error; + if (err) { + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + + /* Initialize wm8775 */ + + /* RESET */ + wm8775_write(sd, R23, 0x000); + /* Disable zero cross detect timeout */ + wm8775_write(sd, R7, 0x000); + /* HPF enable, left justified, 24-bit (Philips) mode */ + wm8775_write(sd, R11, 0x021); + /* Master mode, clock ratio 256fs */ + wm8775_write(sd, R12, 0x102); + /* Powered up */ + wm8775_write(sd, R13, 0x000); + + if (!is_nova_s) { + /* ADC gain +2.5dB, enable zero cross */ + wm8775_write(sd, R14, 0x1d4); + /* ADC gain +2.5dB, enable zero cross */ + wm8775_write(sd, R15, 0x1d4); + /* ALC Stereo, ALC target level -1dB FS max gain +8dB */ + wm8775_write(sd, R16, 0x1bf); + /* Enable gain control, use zero cross detection, + ALC hold time 42.6 ms */ + wm8775_write(sd, R17, 0x185); + } else { + /* ALC stereo, ALC target level -5dB FS, ALC max gain +8dB */ + wm8775_write(sd, R16, 0x1bb); + /* Set ALC mode and hold time */ + wm8775_write(sd, R17, (state->loud->val ? ALC_EN : 0) | ALC_HOLD); + } + /* ALC gain ramp up delay 34 s, ALC gain ramp down delay 33 ms */ + wm8775_write(sd, R18, 0x0a2); + /* Enable noise gate, threshold -72dBfs */ + wm8775_write(sd, R19, 0x005); + if (!is_nova_s) { + /* Transient window 4ms, lower PGA gain limit -1dB */ + wm8775_write(sd, R20, 0x07a); + /* LRBOTH = 1, use input 2. */ + wm8775_write(sd, R21, 0x102); + } else { + /* Transient window 4ms, ALC min gain -5dB */ + wm8775_write(sd, R20, 0x0fb); + + wm8775_set_audio(sd, 1); /* set volume/mute/mux */ + } + return 0; +} + +static int wm8775_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct wm8775_state *state = to_state(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return 0; +} + +static const struct i2c_device_id wm8775_id[] = { + { "wm8775", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8775_id); + +static struct i2c_driver wm8775_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "wm8775", + }, + .probe = wm8775_probe, + .remove = wm8775_remove, + .id_table = wm8775_id, +}; + +module_i2c_driver(wm8775_driver); diff --git a/drivers/media/pci/bt8xx/Makefile b/drivers/media/pci/bt8xx/Makefile index ae347b78fcc..5f06597c6a6 100644 --- a/drivers/media/pci/bt8xx/Makefile +++ b/drivers/media/pci/bt8xx/Makefile @@ -7,5 +7,5 @@ obj-$(CONFIG_DVB_BT8XX) += bt878.o dvb-bt8xx.o dst.o dst_ca.o ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends -ccflags-y += -Idrivers/media/video +ccflags-y += -Idrivers/media/i2c ccflags-y += -Idrivers/media/tuners diff --git a/drivers/media/pci/cx23885/Makefile b/drivers/media/pci/cx23885/Makefile index f92cc4c14f0..a2cbdcf15a8 100644 --- a/drivers/media/pci/cx23885/Makefile +++ b/drivers/media/pci/cx23885/Makefile @@ -7,7 +7,7 @@ cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o \ obj-$(CONFIG_VIDEO_CX23885) += cx23885.o obj-$(CONFIG_MEDIA_ALTERA_CI) += altera-ci.o -ccflags-y += -Idrivers/media/video +ccflags-y += -Idrivers/media/i2c ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/pci/cx25821/Makefile b/drivers/media/pci/cx25821/Makefile index 1434e809480..c038941d605 100644 --- a/drivers/media/pci/cx25821/Makefile +++ b/drivers/media/pci/cx25821/Makefile @@ -7,7 +7,7 @@ cx25821-y := cx25821-core.o cx25821-cards.o cx25821-i2c.o \ obj-$(CONFIG_VIDEO_CX25821) += cx25821.o obj-$(CONFIG_VIDEO_CX25821_ALSA) += cx25821-alsa.o -ccflags-y := -Idrivers/media/video +ccflags-y := -Idrivers/media/i2c ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/pci/cx88/Makefile b/drivers/media/pci/cx88/Makefile index 884b4cdd8ff..d3679c3ee24 100644 --- a/drivers/media/pci/cx88/Makefile +++ b/drivers/media/pci/cx88/Makefile @@ -10,7 +10,7 @@ obj-$(CONFIG_VIDEO_CX88_BLACKBIRD) += cx88-blackbird.o obj-$(CONFIG_VIDEO_CX88_DVB) += cx88-dvb.o obj-$(CONFIG_VIDEO_CX88_VP3054) += cx88-vp3054-i2c.o -ccflags-y += -Idrivers/media/video +ccflags-y += -Idrivers/media/i2c ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/pci/ivtv/Makefile b/drivers/media/pci/ivtv/Makefile index 80b4ec18475..1408c9f1de9 100644 --- a/drivers/media/pci/ivtv/Makefile +++ b/drivers/media/pci/ivtv/Makefile @@ -7,7 +7,7 @@ ivtv-objs := ivtv-routing.o ivtv-cards.o ivtv-controls.o \ obj-$(CONFIG_VIDEO_IVTV) += ivtv.o obj-$(CONFIG_VIDEO_FB_IVTV) += ivtvfb.o -ccflags-y += -I$(srctree)/drivers/media/video +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/Makefile b/drivers/media/pci/saa7134/Makefile index aba50088dcd..9e510c1459f 100644 --- a/drivers/media/pci/saa7134/Makefile +++ b/drivers/media/pci/saa7134/Makefile @@ -10,7 +10,7 @@ obj-$(CONFIG_VIDEO_SAA7134_ALSA) += saa7134-alsa.o obj-$(CONFIG_VIDEO_SAA7134_DVB) += saa7134-dvb.o -ccflags-y += -I$(srctree)/drivers/media/video +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/saa7146/Makefile b/drivers/media/pci/saa7146/Makefile index 362a38b9630..f3566a95e4a 100644 --- a/drivers/media/pci/saa7146/Makefile +++ b/drivers/media/pci/saa7146/Makefile @@ -2,4 +2,4 @@ obj-$(CONFIG_VIDEO_MXB) += mxb.o obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o -ccflags-y += -I$(srctree)/drivers/media/video +ccflags-y += -I$(srctree)/drivers/media/i2c diff --git a/drivers/media/pci/saa7164/Makefile b/drivers/media/pci/saa7164/Makefile index 847110c2e14..ba0e33a1ee2 100644 --- a/drivers/media/pci/saa7164/Makefile +++ b/drivers/media/pci/saa7164/Makefile @@ -4,7 +4,7 @@ saa7164-objs := saa7164-cards.o saa7164-core.o saa7164-i2c.o saa7164-dvb.o \ obj-$(CONFIG_VIDEO_SAA7164) += saa7164.o -ccflags-y += -I$(srctree)/drivers/media/video +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/usb/cx231xx/Makefile b/drivers/media/usb/cx231xx/Makefile index 1d40fce7760..52cf76935e6 100644 --- a/drivers/media/usb/cx231xx/Makefile +++ b/drivers/media/usb/cx231xx/Makefile @@ -8,7 +8,7 @@ obj-$(CONFIG_VIDEO_CX231XX) += cx231xx.o obj-$(CONFIG_VIDEO_CX231XX_ALSA) += cx231xx-alsa.o obj-$(CONFIG_VIDEO_CX231XX_DVB) += cx231xx-dvb.o -ccflags-y += -Idrivers/media/video +ccflags-y += -Idrivers/media/i2c ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/usb/em28xx/Makefile b/drivers/media/usb/em28xx/Makefile index 65c7c29e416..6c5f3381da7 100644 --- a/drivers/media/usb/em28xx/Makefile +++ b/drivers/media/usb/em28xx/Makefile @@ -9,7 +9,7 @@ obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o obj-$(CONFIG_VIDEO_EM28XX_DVB) += em28xx-dvb.o obj-$(CONFIG_VIDEO_EM28XX_RC) += em28xx-rc.o -ccflags-y += -Idrivers/media/video +ccflags-y += -Idrivers/media/i2c ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/usb/hdpvr/Makefile b/drivers/media/usb/hdpvr/Makefile index 52f057f24e3..9b8d1463c52 100644 --- a/drivers/media/usb/hdpvr/Makefile +++ b/drivers/media/usb/hdpvr/Makefile @@ -2,6 +2,6 @@ hdpvr-objs := hdpvr-control.o hdpvr-core.o hdpvr-video.o hdpvr-i2c.o obj-$(CONFIG_VIDEO_HDPVR) += hdpvr.o -ccflags-y += -Idrivers/media/video +ccflags-y += -Idrivers/media/i2c ccflags-y += $(extra-cflags-y) $(extra-cflags-m) diff --git a/drivers/media/usb/pvrusb2/Makefile b/drivers/media/usb/pvrusb2/Makefile index bc716db797e..ad705547bdc 100644 --- a/drivers/media/usb/pvrusb2/Makefile +++ b/drivers/media/usb/pvrusb2/Makefile @@ -16,7 +16,7 @@ pvrusb2-objs := pvrusb2-i2c-core.o \ obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o -ccflags-y += -Idrivers/media/video +ccflags-y += -Idrivers/media/i2c ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/usb/stk1160/Makefile b/drivers/media/usb/stk1160/Makefile index 8a3c78482e7..dfe3e90ff39 100644 --- a/drivers/media/usb/stk1160/Makefile +++ b/drivers/media/usb/stk1160/Makefile @@ -8,4 +8,4 @@ stk1160-y := stk1160-core.o \ obj-$(CONFIG_VIDEO_STK1160) += stk1160.o -ccflags-y += -Idrivers/media/video +ccflags-y += -Idrivers/media/i2c diff --git a/drivers/media/usb/tlg2300/Makefile b/drivers/media/usb/tlg2300/Makefile index 4d660879999..137f8e38cde 100644 --- a/drivers/media/usb/tlg2300/Makefile +++ b/drivers/media/usb/tlg2300/Makefile @@ -2,7 +2,7 @@ poseidon-objs := pd-video.o pd-alsa.o pd-dvb.o pd-radio.o pd-main.o obj-$(CONFIG_VIDEO_TLG2300) += poseidon.o -ccflags-y += -Idrivers/media/video +ccflags-y += -Idrivers/media/i2c ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/usb/tm6000/Makefile b/drivers/media/usb/tm6000/Makefile index 1feb8c9c816..6fa1f104451 100644 --- a/drivers/media/usb/tm6000/Makefile +++ b/drivers/media/usb/tm6000/Makefile @@ -9,7 +9,7 @@ obj-$(CONFIG_VIDEO_TM6000) += tm6000.o obj-$(CONFIG_VIDEO_TM6000_ALSA) += tm6000-alsa.o obj-$(CONFIG_VIDEO_TM6000_DVB) += tm6000-dvb.o -ccflags-y := -Idrivers/media/video +ccflags-y := -Idrivers/media/i2c ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/usb/usbvision/Makefile b/drivers/media/usb/usbvision/Makefile index d55c6bd97a3..9b3a5581df4 100644 --- a/drivers/media/usb/usbvision/Makefile +++ b/drivers/media/usb/usbvision/Makefile @@ -2,5 +2,5 @@ usbvision-objs := usbvision-core.o usbvision-video.o usbvision-i2c.o usbvision- obj-$(CONFIG_VIDEO_USBVISION) += usbvision.o -ccflags-y += -Idrivers/media/video +ccflags-y += -Idrivers/media/i2c ccflags-y += -Idrivers/media/tuners diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index a7bd9576ccd..f2171e777dd 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -1,578 +1,4 @@ -# -# Generic video config states -# - -config VIDEO_BTCX - depends on PCI - tristate - -config VIDEO_TVEEPROM - tristate - depends on I2C - -# -# Multimedia Video device configuration -# - -menuconfig VIDEO_CAPTURE_DRIVERS - bool "Video capture adapters" - depends on VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT - default y - ---help--- - Say Y here to enable selecting the video adapters for - webcams, analog TV, and hybrid analog/digital TV. - Some of those devices also supports FM radio. - -if VIDEO_CAPTURE_DRIVERS && VIDEO_V4L2 - -config VIDEO_HELPER_CHIPS_AUTO - bool "Autoselect pertinent encoders/decoders and other helper chips" - default y if !EXPERT - ---help--- - Most video cards may require additional modules to encode or - decode audio/video standards. This option will autoselect - all pertinent modules to each selected video module. - - Unselect this only if you know exactly what you are doing, since - it may break support on some boards. - - In doubt, say Y. - -config VIDEO_IR_I2C - tristate "I2C module for IR" if !VIDEO_HELPER_CHIPS_AUTO - depends on I2C && RC_CORE - default y - ---help--- - Most boards have an IR chip directly connected via GPIO. However, - some video boards have the IR connected via I2C bus. - - If your board doesn't have an I2C IR chip, you may disable this - option. - - In doubt, say Y. - -# -# Encoder / Decoder module configuration -# - -menu "Encoders, decoders, sensors and other helper chips" - visible if !VIDEO_HELPER_CHIPS_AUTO - -comment "Audio decoders, processors and mixers" - -config VIDEO_TVAUDIO - tristate "Simple audio decoder chips" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for several audio decoder chips found on some bt8xx boards: - Philips: tda9840, tda9873h, tda9874h/a, tda9850, tda985x, tea6300, - tea6320, tea6420, tda8425, ta8874z. - Microchip: pic16c54 based design on ProVideo PV951 board. - - To compile this driver as a module, choose M here: the - module will be called tvaudio. - -config VIDEO_TDA7432 - tristate "Philips TDA7432 audio processor" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for tda7432 audio decoder chip found on some bt8xx boards. - - To compile this driver as a module, choose M here: the - module will be called tda7432. - -config VIDEO_TDA9840 - tristate "Philips TDA9840 audio processor" - depends on I2C - ---help--- - Support for tda9840 audio decoder chip found on some Zoran boards. - - To compile this driver as a module, choose M here: the - module will be called tda9840. - -config VIDEO_TEA6415C - tristate "Philips TEA6415C audio processor" - depends on I2C - ---help--- - Support for tea6415c audio decoder chip found on some bt8xx boards. - - To compile this driver as a module, choose M here: the - module will be called tea6415c. - -config VIDEO_TEA6420 - tristate "Philips TEA6420 audio processor" - depends on I2C - ---help--- - Support for tea6420 audio decoder chip found on some bt8xx boards. - - To compile this driver as a module, choose M here: the - module will be called tea6420. - -config VIDEO_MSP3400 - tristate "Micronas MSP34xx audio decoders" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Micronas MSP34xx series of audio decoders. - - To compile this driver as a module, choose M here: the - module will be called msp3400. - -config VIDEO_CS5345 - tristate "Cirrus Logic CS5345 audio ADC" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Cirrus Logic CS5345 24-bit, 192 kHz - stereo A/D converter. - - To compile this driver as a module, choose M here: the - module will be called cs5345. - -config VIDEO_CS53L32A - tristate "Cirrus Logic CS53L32A audio ADC" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Cirrus Logic CS53L32A low voltage - stereo A/D converter. - - To compile this driver as a module, choose M here: the - module will be called cs53l32a. - -config VIDEO_TLV320AIC23B - tristate "Texas Instruments TLV320AIC23B audio codec" - depends on VIDEO_V4L2 && I2C && EXPERIMENTAL - ---help--- - Support for the Texas Instruments TLV320AIC23B audio codec. - - To compile this driver as a module, choose M here: the - module will be called tlv320aic23b. - -config VIDEO_WM8775 - tristate "Wolfson Microelectronics WM8775 audio ADC with input mixer" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Wolfson Microelectronics WM8775 high - performance stereo A/D Converter with a 4 channel input mixer. - - To compile this driver as a module, choose M here: the - module will be called wm8775. - -config VIDEO_WM8739 - tristate "Wolfson Microelectronics WM8739 stereo audio ADC" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Wolfson Microelectronics WM8739 - stereo A/D Converter. - - To compile this driver as a module, choose M here: the - module will be called wm8739. - -config VIDEO_VP27SMPX - tristate "Panasonic VP27s internal MPX" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the internal MPX of the Panasonic VP27s tuner. - - To compile this driver as a module, choose M here: the - module will be called vp27smpx. - -comment "RDS decoders" - -config VIDEO_SAA6588 - tristate "SAA6588 Radio Chip RDS decoder support" - depends on VIDEO_V4L2 && I2C - - help - Support for this Radio Data System (RDS) decoder. This allows - seeing radio station identification transmitted using this - standard. - - To compile this driver as a module, choose M here: the - module will be called saa6588. - -comment "Video decoders" - -config VIDEO_ADV7180 - tristate "Analog Devices ADV7180 decoder" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Analog Devices ADV7180 video decoder. - - To compile this driver as a module, choose M here: the - module will be called adv7180. - -config VIDEO_ADV7183 - tristate "Analog Devices ADV7183 decoder" - depends on VIDEO_V4L2 && I2C - ---help--- - V4l2 subdevice driver for the Analog Devices - ADV7183 video decoder. - - To compile this driver as a module, choose M here: the - module will be called adv7183. - -config VIDEO_BT819 - tristate "BT819A VideoStream decoder" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for BT819A video decoder. - - To compile this driver as a module, choose M here: the - module will be called bt819. - -config VIDEO_BT856 - tristate "BT856 VideoStream decoder" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for BT856 video decoder. - - To compile this driver as a module, choose M here: the - module will be called bt856. - -config VIDEO_BT866 - tristate "BT866 VideoStream decoder" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for BT866 video decoder. - - To compile this driver as a module, choose M here: the - module will be called bt866. - -config VIDEO_KS0127 - tristate "KS0127 video decoder" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for KS0127 video decoder. - - This chip is used on AverMedia AVS6EYES Zoran-based MJPEG - cards. - - To compile this driver as a module, choose M here: the - module will be called ks0127. - -config VIDEO_SAA7110 - tristate "Philips SAA7110 video decoder" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Philips SAA7110 video decoders. - - To compile this driver as a module, choose M here: the - module will be called saa7110. - -config VIDEO_SAA711X - tristate "Philips SAA7111/3/4/5 video decoders" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Philips SAA7111/3/4/5 video decoders. - - To compile this driver as a module, choose M here: the - module will be called saa7115. - -config VIDEO_SAA7191 - tristate "Philips SAA7191 video decoder" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Philips SAA7191 video decoder. - - To compile this driver as a module, choose M here: the - module will be called saa7191. - -config VIDEO_TVP514X - tristate "Texas Instruments TVP514x video decoder" - depends on VIDEO_V4L2 && I2C - ---help--- - This is a Video4Linux2 sensor-level driver for the TI TVP5146/47 - decoder. It is currently working with the TI OMAP3 camera - controller. - - To compile this driver as a module, choose M here: the - module will be called tvp514x. - -config VIDEO_TVP5150 - tristate "Texas Instruments TVP5150 video decoder" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Texas Instruments TVP5150 video decoder. - - To compile this driver as a module, choose M here: the - module will be called tvp5150. - -config VIDEO_TVP7002 - tristate "Texas Instruments TVP7002 video decoder" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Texas Instruments TVP7002 video decoder. - - To compile this driver as a module, choose M here: the - module will be called tvp7002. - -config VIDEO_VPX3220 - tristate "vpx3220a, vpx3216b & vpx3214c video decoders" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for VPX322x video decoders. - - To compile this driver as a module, choose M here: the - module will be called vpx3220. - -comment "Video and audio decoders" - -config VIDEO_SAA717X - tristate "Philips SAA7171/3/4 audio/video decoders" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Philips SAA7171/3/4 audio/video decoders. - - To compile this driver as a module, choose M here: the - module will be called saa717x. - -source "drivers/media/video/cx25840/Kconfig" - -comment "MPEG video encoders" - -config VIDEO_CX2341X - tristate "Conexant CX2341x MPEG encoders" - depends on VIDEO_V4L2 && VIDEO_V4L2_COMMON - ---help--- - Support for the Conexant CX23416 MPEG encoders - and CX23415 MPEG encoder/decoders. - - This module currently supports the encoding functions only. - - To compile this driver as a module, choose M here: the - module will be called cx2341x. - -comment "Video encoders" - -config VIDEO_SAA7127 - tristate "Philips SAA7127/9 digital video encoders" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Philips SAA7127/9 digital video encoders. - - To compile this driver as a module, choose M here: the - module will be called saa7127. - -config VIDEO_SAA7185 - tristate "Philips SAA7185 video encoder" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Philips SAA7185 video encoder. - - To compile this driver as a module, choose M here: the - module will be called saa7185. - -config VIDEO_ADV7170 - tristate "Analog Devices ADV7170 video encoder" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Analog Devices ADV7170 video encoder driver - - To compile this driver as a module, choose M here: the - module will be called adv7170. - -config VIDEO_ADV7175 - tristate "Analog Devices ADV7175 video encoder" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Analog Devices ADV7175 video encoder driver - - To compile this driver as a module, choose M here: the - module will be called adv7175. - -config VIDEO_ADV7343 - tristate "ADV7343 video encoder" - depends on I2C - help - Support for Analog Devices I2C bus based ADV7343 encoder. - - To compile this driver as a module, choose M here: the - module will be called adv7343. - -config VIDEO_ADV7393 - tristate "ADV7393 video encoder" - depends on I2C - help - Support for Analog Devices I2C bus based ADV7393 encoder. - - To compile this driver as a module, choose M here: the - module will be called adv7393. - -config VIDEO_AK881X - tristate "AK8813/AK8814 video encoders" - depends on I2C - help - Video output driver for AKM AK8813 and AK8814 TV encoders - -comment "Camera sensor devices" - -config VIDEO_APTINA_PLL - tristate - -config VIDEO_SMIAPP_PLL - tristate - -config VIDEO_OV7670 - tristate "OmniVision OV7670 sensor support" - depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT - ---help--- - This is a Video4Linux2 sensor-level driver for the OmniVision - OV7670 VGA camera. It currently only works with the M88ALP01 - controller. - -config VIDEO_VS6624 - tristate "ST VS6624 sensor support" - depends on VIDEO_V4L2 && I2C - depends on MEDIA_CAMERA_SUPPORT - ---help--- - This is a Video4Linux2 sensor-level driver for the ST VS6624 - camera. - - To compile this driver as a module, choose M here: the - module will be called vs6624. - -config VIDEO_MT9M032 - tristate "MT9M032 camera sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT - select VIDEO_APTINA_PLL - ---help--- - This driver supports MT9M032 camera sensors from Aptina, monochrome - models only. - -config VIDEO_MT9P031 - tristate "Aptina MT9P031 support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT - select VIDEO_APTINA_PLL - ---help--- - This is a Video4Linux2 sensor-level driver for the Aptina - (Micron) mt9p031 5 Mpixel camera. - -config VIDEO_MT9T001 - tristate "Aptina MT9T001 support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT - ---help--- - This is a Video4Linux2 sensor-level driver for the Aptina - (Micron) mt0t001 3 Mpixel camera. - -config VIDEO_MT9V011 - tristate "Micron mt9v011 sensor support" - depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT - ---help--- - This is a Video4Linux2 sensor-level driver for the Micron - mt0v011 1.3 Mpixel camera. It currently only works with the - em28xx driver. - -config VIDEO_MT9V032 - tristate "Micron MT9V032 sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT - ---help--- - This is a Video4Linux2 sensor-level driver for the Micron - MT9V032 752x480 CMOS sensor. - -config VIDEO_TCM825X - tristate "TCM825x camera sensor support" - depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT - ---help--- - This is a driver for the Toshiba TCM825x VGA camera sensor. - It is used for example in Nokia N800. - -config VIDEO_SR030PC30 - tristate "Siliconfile SR030PC30 sensor support" - depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT - ---help--- - This driver supports SR030PC30 VGA camera from Siliconfile - -config VIDEO_NOON010PC30 - tristate "Siliconfile NOON010PC30 sensor support" - depends on I2C && VIDEO_V4L2 && EXPERIMENTAL && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT - ---help--- - This driver supports NOON010PC30 CIF camera from Siliconfile - -source "drivers/media/video/m5mols/Kconfig" - -config VIDEO_S5K6AA - tristate "Samsung S5K6AAFX sensor support" - depends on MEDIA_CAMERA_SUPPORT - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - ---help--- - This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M - camera sensor with an embedded SoC image signal processor. - -source "drivers/media/video/smiapp/Kconfig" - -comment "Flash devices" - -config VIDEO_ADP1653 - tristate "ADP1653 flash support" - depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER - depends on MEDIA_CAMERA_SUPPORT - ---help--- - This is a driver for the ADP1653 flash controller. It is used for - example in Nokia N900. - -config VIDEO_AS3645A - tristate "AS3645A flash driver support" - depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER - depends on MEDIA_CAMERA_SUPPORT - ---help--- - This is a driver for the AS3645A and LM3555 flash controllers. It has - build in control for flash, torch and indicator LEDs. - -comment "Video improvement chips" - -config VIDEO_UPD64031A - tristate "NEC Electronics uPD64031A Ghost Reduction" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the NEC Electronics uPD64031A Ghost Reduction - video chip. It is most often found in NTSC TV cards made for - Japan and is used to reduce the 'ghosting' effect that can - be present in analog TV broadcasts. - - To compile this driver as a module, choose M here: the - module will be called upd64031a. - -config VIDEO_UPD64083 - tristate "NEC Electronics uPD64083 3-Dimensional Y/C separation" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the NEC Electronics uPD64083 3-Dimensional Y/C - separation video chip. It is used to improve the quality of - the colors of a composite signal. - - To compile this driver as a module, choose M here: the - module will be called upd64083. - -comment "Miscelaneous helper chips" - -config VIDEO_THS7303 - tristate "THS7303 Video Amplifier" - depends on I2C - help - Support for TI THS7303 video amplifier - - To compile this driver as a module, choose M here: the - module will be called ths7303. - -config VIDEO_M52790 - tristate "Mitsubishi M52790 A/V switch" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Mitsubishi M52790 A/V switch. - - To compile this driver as a module, choose M here: the - module will be called m52790. - -endmenu # encoder / decoder chips +if MEDIA_CAMERA_SUPPORT config VIDEO_VIVI tristate "Virtual Video Driver" @@ -877,7 +303,6 @@ source "drivers/media/video/s5p-fimc/Kconfig" source "drivers/media/video/s5p-tv/Kconfig" endif # V4L_PLATFORM_DRIVERS -endif # VIDEO_CAPTURE_DRIVERS menuconfig V4L_MEM2MEM_DRIVERS bool "Memory-to-memory multimedia devices" @@ -955,3 +380,5 @@ config VIDEO_MX2_EMMAPRP conversion. endif # V4L_MEM2MEM_DRIVERS + +endif # MEDIA_CAMERA_SUPPORT diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index a0c66923fcd..52a04faa60e 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -2,73 +2,9 @@ # Makefile for the video capture/playback device drivers. # -msp3400-objs := msp3400-driver.o msp3400-kthreads.o - omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o -# Helper modules - -obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o - -# All i2c modules must come first: - -obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o -obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o -obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o -obj-$(CONFIG_VIDEO_TDA9840) += tda9840.o -obj-$(CONFIG_VIDEO_TEA6415C) += tea6415c.o -obj-$(CONFIG_VIDEO_TEA6420) += tea6420.o -obj-$(CONFIG_VIDEO_SAA7110) += saa7110.o -obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o -obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o -obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o -obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o -obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o -obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o -obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o -obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o -obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o -obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o -obj-$(CONFIG_VIDEO_ADV7393) += adv7393.o -obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o -obj-$(CONFIG_VIDEO_VS6624) += vs6624.o -obj-$(CONFIG_VIDEO_BT819) += bt819.o -obj-$(CONFIG_VIDEO_BT856) += bt856.o -obj-$(CONFIG_VIDEO_BT866) += bt866.o -obj-$(CONFIG_VIDEO_KS0127) += ks0127.o -obj-$(CONFIG_VIDEO_THS7303) += ths7303.o obj-$(CONFIG_VIDEO_VINO) += indycam.o -obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o -obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o -obj-$(CONFIG_VIDEO_TVP7002) += tvp7002.o -obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o -obj-$(CONFIG_VIDEO_CS5345) += cs5345.o -obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o -obj-$(CONFIG_VIDEO_M52790) += m52790.o -obj-$(CONFIG_VIDEO_TLV320AIC23B) += tlv320aic23b.o -obj-$(CONFIG_VIDEO_WM8775) += wm8775.o -obj-$(CONFIG_VIDEO_WM8739) += wm8739.o -obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o -obj-$(CONFIG_VIDEO_CX25840) += cx25840/ -obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o -obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o -obj-$(CONFIG_VIDEO_OV7670) += ov7670.o -obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o -obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o -obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o -obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o -obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o -obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o -obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o -obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o -obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o -obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/ -obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o -obj-$(CONFIG_VIDEO_SMIAPP) += smiapp/ -obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o -obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o - -obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o @@ -85,16 +21,12 @@ obj-$(CONFIG_SOC_CAMERA_OV9740) += ov9740.o obj-$(CONFIG_SOC_CAMERA_RJ54N1) += rj54n1cb0c.o obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o -# And now the v4l2 drivers: - obj-$(CONFIG_VIDEO_VINO) += vino.o obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o -obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o -obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/ obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/ @@ -107,7 +39,6 @@ obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o obj-$(CONFIG_VIDEO_VIVI) += vivi.o obj-$(CONFIG_VIDEO_MEM2MEM_TESTDEV) += mem2mem_testdev.o -obj-$(CONFIG_VIDEO_AK881X) += ak881x.o obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o @@ -140,8 +71,6 @@ obj-$(CONFIG_ARCH_DAVINCI) += davinci/ obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o -obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o - obj-y += davinci/ obj-$(CONFIG_ARCH_OMAP) += omap/ diff --git a/drivers/media/video/adp1653.c b/drivers/media/video/adp1653.c deleted file mode 100644 index 57e87090388..00000000000 --- a/drivers/media/video/adp1653.c +++ /dev/null @@ -1,487 +0,0 @@ -/* - * drivers/media/video/adp1653.c - * - * Copyright (C) 2008--2011 Nokia Corporation - * - * Contact: Sakari Ailus - * - * Contributors: - * Sakari Ailus - * Tuukka Toivonen - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - * TODO: - * - fault interrupt handling - * - hardware strobe - * - power doesn't need to be ON if all lights are off - * - */ - -#include -#include -#include -#include -#include -#include - -#define TIMEOUT_MAX 820000 -#define TIMEOUT_STEP 54600 -#define TIMEOUT_MIN (TIMEOUT_MAX - ADP1653_REG_CONFIG_TMR_SET_MAX \ - * TIMEOUT_STEP) -#define TIMEOUT_US_TO_CODE(t) ((TIMEOUT_MAX + (TIMEOUT_STEP / 2) - (t)) \ - / TIMEOUT_STEP) -#define TIMEOUT_CODE_TO_US(c) (TIMEOUT_MAX - (c) * TIMEOUT_STEP) - -/* Write values into ADP1653 registers. */ -static int adp1653_update_hw(struct adp1653_flash *flash) -{ - struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); - u8 out_sel; - u8 config = 0; - int rval; - - out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG( - flash->indicator_intensity->val) - << ADP1653_REG_OUT_SEL_ILED_SHIFT; - - switch (flash->led_mode->val) { - case V4L2_FLASH_LED_MODE_NONE: - break; - case V4L2_FLASH_LED_MODE_FLASH: - /* Flash mode, light on with strobe, duration from timer */ - config = ADP1653_REG_CONFIG_TMR_CFG; - config |= TIMEOUT_US_TO_CODE(flash->flash_timeout->val) - << ADP1653_REG_CONFIG_TMR_SET_SHIFT; - break; - case V4L2_FLASH_LED_MODE_TORCH: - /* Torch mode, light immediately on, duration indefinite */ - out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG( - flash->torch_intensity->val) - << ADP1653_REG_OUT_SEL_HPLED_SHIFT; - break; - } - - rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel); - if (rval < 0) - return rval; - - rval = i2c_smbus_write_byte_data(client, ADP1653_REG_CONFIG, config); - if (rval < 0) - return rval; - - return 0; -} - -static int adp1653_get_fault(struct adp1653_flash *flash) -{ - struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); - int fault; - int rval; - - fault = i2c_smbus_read_byte_data(client, ADP1653_REG_FAULT); - if (IS_ERR_VALUE(fault)) - return fault; - - flash->fault |= fault; - - if (!flash->fault) - return 0; - - /* Clear faults. */ - rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0); - if (IS_ERR_VALUE(rval)) - return rval; - - flash->led_mode->val = V4L2_FLASH_LED_MODE_NONE; - - rval = adp1653_update_hw(flash); - if (IS_ERR_VALUE(rval)) - return rval; - - return flash->fault; -} - -static int adp1653_strobe(struct adp1653_flash *flash, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); - u8 out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG( - flash->indicator_intensity->val) - << ADP1653_REG_OUT_SEL_ILED_SHIFT; - int rval; - - if (flash->led_mode->val != V4L2_FLASH_LED_MODE_FLASH) - return -EBUSY; - - if (!enable) - return i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, - out_sel); - - out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG( - flash->flash_intensity->val) - << ADP1653_REG_OUT_SEL_HPLED_SHIFT; - rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel); - if (rval) - return rval; - - /* Software strobe using i2c */ - rval = i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE, - ADP1653_REG_SW_STROBE_SW_STROBE); - if (rval) - return rval; - return i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE, 0); -} - -/* -------------------------------------------------------------------------- - * V4L2 controls - */ - -static int adp1653_get_ctrl(struct v4l2_ctrl *ctrl) -{ - struct adp1653_flash *flash = - container_of(ctrl->handler, struct adp1653_flash, ctrls); - int rval; - - rval = adp1653_get_fault(flash); - if (IS_ERR_VALUE(rval)) - return rval; - - ctrl->cur.val = 0; - - if (flash->fault & ADP1653_REG_FAULT_FLT_SCP) - ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT; - if (flash->fault & ADP1653_REG_FAULT_FLT_OT) - ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE; - if (flash->fault & ADP1653_REG_FAULT_FLT_TMR) - ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT; - if (flash->fault & ADP1653_REG_FAULT_FLT_OV) - ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE; - - flash->fault = 0; - - return 0; -} - -static int adp1653_set_ctrl(struct v4l2_ctrl *ctrl) -{ - struct adp1653_flash *flash = - container_of(ctrl->handler, struct adp1653_flash, ctrls); - int rval; - - rval = adp1653_get_fault(flash); - if (IS_ERR_VALUE(rval)) - return rval; - if ((rval & (ADP1653_REG_FAULT_FLT_SCP | - ADP1653_REG_FAULT_FLT_OT | - ADP1653_REG_FAULT_FLT_OV)) && - (ctrl->id == V4L2_CID_FLASH_STROBE || - ctrl->id == V4L2_CID_FLASH_TORCH_INTENSITY || - ctrl->id == V4L2_CID_FLASH_LED_MODE)) - return -EBUSY; - - switch (ctrl->id) { - case V4L2_CID_FLASH_STROBE: - return adp1653_strobe(flash, 1); - case V4L2_CID_FLASH_STROBE_STOP: - return adp1653_strobe(flash, 0); - } - - return adp1653_update_hw(flash); -} - -static const struct v4l2_ctrl_ops adp1653_ctrl_ops = { - .g_volatile_ctrl = adp1653_get_ctrl, - .s_ctrl = adp1653_set_ctrl, -}; - -static int adp1653_init_controls(struct adp1653_flash *flash) -{ - struct v4l2_ctrl *fault; - - v4l2_ctrl_handler_init(&flash->ctrls, 9); - - flash->led_mode = - v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops, - V4L2_CID_FLASH_LED_MODE, - V4L2_FLASH_LED_MODE_TORCH, ~0x7, 0); - v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops, - V4L2_CID_FLASH_STROBE_SOURCE, - V4L2_FLASH_STROBE_SOURCE_SOFTWARE, ~0x1, 0); - v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, - V4L2_CID_FLASH_STROBE, 0, 0, 0, 0); - v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, - V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0); - flash->flash_timeout = - v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, - V4L2_CID_FLASH_TIMEOUT, TIMEOUT_MIN, - flash->platform_data->max_flash_timeout, - TIMEOUT_STEP, - flash->platform_data->max_flash_timeout); - flash->flash_intensity = - v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, - V4L2_CID_FLASH_INTENSITY, - ADP1653_FLASH_INTENSITY_MIN, - flash->platform_data->max_flash_intensity, - 1, flash->platform_data->max_flash_intensity); - flash->torch_intensity = - v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, - V4L2_CID_FLASH_TORCH_INTENSITY, - ADP1653_TORCH_INTENSITY_MIN, - flash->platform_data->max_torch_intensity, - ADP1653_FLASH_INTENSITY_STEP, - flash->platform_data->max_torch_intensity); - flash->indicator_intensity = - v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, - V4L2_CID_FLASH_INDICATOR_INTENSITY, - ADP1653_INDICATOR_INTENSITY_MIN, - flash->platform_data->max_indicator_intensity, - ADP1653_INDICATOR_INTENSITY_STEP, - ADP1653_INDICATOR_INTENSITY_MIN); - fault = v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, - V4L2_CID_FLASH_FAULT, 0, - V4L2_FLASH_FAULT_OVER_VOLTAGE - | V4L2_FLASH_FAULT_OVER_TEMPERATURE - | V4L2_FLASH_FAULT_SHORT_CIRCUIT, 0, 0); - - if (flash->ctrls.error) - return flash->ctrls.error; - - fault->flags |= V4L2_CTRL_FLAG_VOLATILE; - - flash->subdev.ctrl_handler = &flash->ctrls; - return 0; -} - -/* -------------------------------------------------------------------------- - * V4L2 subdev operations - */ - -static int -adp1653_init_device(struct adp1653_flash *flash) -{ - struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); - int rval; - - /* Clear FAULT register by writing zero to OUT_SEL */ - rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0); - if (rval < 0) { - dev_err(&client->dev, "failed writing fault register\n"); - return -EIO; - } - - mutex_lock(flash->ctrls.lock); - /* Reset faults before reading new ones. */ - flash->fault = 0; - rval = adp1653_get_fault(flash); - mutex_unlock(flash->ctrls.lock); - if (rval > 0) { - dev_err(&client->dev, "faults detected: 0x%1.1x\n", rval); - return -EIO; - } - - mutex_lock(flash->ctrls.lock); - rval = adp1653_update_hw(flash); - mutex_unlock(flash->ctrls.lock); - if (rval) { - dev_err(&client->dev, - "adp1653_update_hw failed at %s\n", __func__); - return -EIO; - } - - return 0; -} - -static int -__adp1653_set_power(struct adp1653_flash *flash, int on) -{ - int ret; - - ret = flash->platform_data->power(&flash->subdev, on); - if (ret < 0) - return ret; - - if (!on) - return 0; - - ret = adp1653_init_device(flash); - if (ret < 0) - flash->platform_data->power(&flash->subdev, 0); - - return ret; -} - -static int -adp1653_set_power(struct v4l2_subdev *subdev, int on) -{ - struct adp1653_flash *flash = to_adp1653_flash(subdev); - int ret = 0; - - mutex_lock(&flash->power_lock); - - /* If the power count is modified from 0 to != 0 or from != 0 to 0, - * update the power state. - */ - if (flash->power_count == !on) { - ret = __adp1653_set_power(flash, !!on); - if (ret < 0) - goto done; - } - - /* Update the power count. */ - flash->power_count += on ? 1 : -1; - WARN_ON(flash->power_count < 0); - -done: - mutex_unlock(&flash->power_lock); - return ret; -} - -static int adp1653_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - return adp1653_set_power(sd, 1); -} - -static int adp1653_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - return adp1653_set_power(sd, 0); -} - -static const struct v4l2_subdev_core_ops adp1653_core_ops = { - .s_power = adp1653_set_power, -}; - -static const struct v4l2_subdev_ops adp1653_ops = { - .core = &adp1653_core_ops, -}; - -static const struct v4l2_subdev_internal_ops adp1653_internal_ops = { - .open = adp1653_open, - .close = adp1653_close, -}; - -/* -------------------------------------------------------------------------- - * I2C driver - */ -#ifdef CONFIG_PM - -static int adp1653_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct adp1653_flash *flash = to_adp1653_flash(subdev); - - if (!flash->power_count) - return 0; - - return __adp1653_set_power(flash, 0); -} - -static int adp1653_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct adp1653_flash *flash = to_adp1653_flash(subdev); - - if (!flash->power_count) - return 0; - - return __adp1653_set_power(flash, 1); -} - -#else - -#define adp1653_suspend NULL -#define adp1653_resume NULL - -#endif /* CONFIG_PM */ - -static int adp1653_probe(struct i2c_client *client, - const struct i2c_device_id *devid) -{ - struct adp1653_flash *flash; - int ret; - - /* we couldn't work without platform data */ - if (client->dev.platform_data == NULL) - return -ENODEV; - - flash = kzalloc(sizeof(*flash), GFP_KERNEL); - if (flash == NULL) - return -ENOMEM; - - flash->platform_data = client->dev.platform_data; - - mutex_init(&flash->power_lock); - - v4l2_i2c_subdev_init(&flash->subdev, client, &adp1653_ops); - flash->subdev.internal_ops = &adp1653_internal_ops; - flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - ret = adp1653_init_controls(flash); - if (ret) - goto free_and_quit; - - ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0); - if (ret < 0) - goto free_and_quit; - - flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; - - return 0; - -free_and_quit: - v4l2_ctrl_handler_free(&flash->ctrls); - kfree(flash); - return ret; -} - -static int __exit adp1653_remove(struct i2c_client *client) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct adp1653_flash *flash = to_adp1653_flash(subdev); - - v4l2_device_unregister_subdev(&flash->subdev); - v4l2_ctrl_handler_free(&flash->ctrls); - media_entity_cleanup(&flash->subdev.entity); - kfree(flash); - return 0; -} - -static const struct i2c_device_id adp1653_id_table[] = { - { ADP1653_NAME, 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, adp1653_id_table); - -static struct dev_pm_ops adp1653_pm_ops = { - .suspend = adp1653_suspend, - .resume = adp1653_resume, -}; - -static struct i2c_driver adp1653_i2c_driver = { - .driver = { - .name = ADP1653_NAME, - .pm = &adp1653_pm_ops, - }, - .probe = adp1653_probe, - .remove = __exit_p(adp1653_remove), - .id_table = adp1653_id_table, -}; - -module_i2c_driver(adp1653_i2c_driver); - -MODULE_AUTHOR("Sakari Ailus "); -MODULE_DESCRIPTION("Analog Devices ADP1653 LED flash driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/adv7170.c b/drivers/media/video/adv7170.c deleted file mode 100644 index 6bc01fb98ff..00000000000 --- a/drivers/media/video/adv7170.c +++ /dev/null @@ -1,410 +0,0 @@ -/* - * adv7170 - adv7170, adv7171 video encoder driver version 0.0.1 - * - * Copyright (C) 2002 Maxim Yevtyushkin - * - * Based on adv7176 driver by: - * - * Copyright (C) 1998 Dave Perks - * Copyright (C) 1999 Wolfgang Scherr - * Copyright (C) 2000 Serguei Miridonov - * - some corrections for Pinnacle Systems Inc. DC10plus card. - * - * Changes by Ronald Bultje - * - moved over to linux>=2.4.x i2c protocol (1/1/2003) - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("Analog Devices ADV7170 video encoder driver"); -MODULE_AUTHOR("Maxim Yevtyushkin"); -MODULE_LICENSE("GPL"); - - -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - -/* ----------------------------------------------------------------------- */ - -struct adv7170 { - struct v4l2_subdev sd; - unsigned char reg[128]; - - v4l2_std_id norm; - int input; -}; - -static inline struct adv7170 *to_adv7170(struct v4l2_subdev *sd) -{ - return container_of(sd, struct adv7170, sd); -} - -static char *inputs[] = { "pass_through", "play_back" }; - -static enum v4l2_mbus_pixelcode adv7170_codes[] = { - V4L2_MBUS_FMT_UYVY8_2X8, - V4L2_MBUS_FMT_UYVY8_1X16, -}; - -/* ----------------------------------------------------------------------- */ - -static inline int adv7170_write(struct v4l2_subdev *sd, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct adv7170 *encoder = to_adv7170(sd); - - encoder->reg[reg] = value; - return i2c_smbus_write_byte_data(client, reg, value); -} - -static inline int adv7170_read(struct v4l2_subdev *sd, u8 reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_read_byte_data(client, reg); -} - -static int adv7170_write_block(struct v4l2_subdev *sd, - const u8 *data, unsigned int len) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct adv7170 *encoder = to_adv7170(sd); - int ret = -1; - u8 reg; - - /* the adv7170 has an autoincrement function, use it if - * the adapter understands raw I2C */ - if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - /* do raw I2C, not smbus compatible */ - u8 block_data[32]; - int block_len; - - while (len >= 2) { - block_len = 0; - block_data[block_len++] = reg = data[0]; - do { - block_data[block_len++] = - encoder->reg[reg++] = data[1]; - len -= 2; - data += 2; - } while (len >= 2 && data[0] == reg && block_len < 32); - ret = i2c_master_send(client, block_data, block_len); - if (ret < 0) - break; - } - } else { - /* do some slow I2C emulation kind of thing */ - while (len >= 2) { - reg = *data++; - ret = adv7170_write(sd, reg, *data++); - if (ret < 0) - break; - len -= 2; - } - } - return ret; -} - -/* ----------------------------------------------------------------------- */ - -#define TR0MODE 0x4c -#define TR0RST 0x80 - -#define TR1CAPT 0x00 -#define TR1PLAY 0x00 - -static const unsigned char init_NTSC[] = { - 0x00, 0x10, /* MR0 */ - 0x01, 0x20, /* MR1 */ - 0x02, 0x0e, /* MR2 RTC control: bits 2 and 1 */ - 0x03, 0x80, /* MR3 */ - 0x04, 0x30, /* MR4 */ - 0x05, 0x00, /* Reserved */ - 0x06, 0x00, /* Reserved */ - 0x07, TR0MODE, /* TM0 */ - 0x08, TR1CAPT, /* TM1 */ - 0x09, 0x16, /* Fsc0 */ - 0x0a, 0x7c, /* Fsc1 */ - 0x0b, 0xf0, /* Fsc2 */ - 0x0c, 0x21, /* Fsc3 */ - 0x0d, 0x00, /* Subcarrier Phase */ - 0x0e, 0x00, /* Closed Capt. Ext 0 */ - 0x0f, 0x00, /* Closed Capt. Ext 1 */ - 0x10, 0x00, /* Closed Capt. 0 */ - 0x11, 0x00, /* Closed Capt. 1 */ - 0x12, 0x00, /* Pedestal Ctl 0 */ - 0x13, 0x00, /* Pedestal Ctl 1 */ - 0x14, 0x00, /* Pedestal Ctl 2 */ - 0x15, 0x00, /* Pedestal Ctl 3 */ - 0x16, 0x00, /* CGMS_WSS_0 */ - 0x17, 0x00, /* CGMS_WSS_1 */ - 0x18, 0x00, /* CGMS_WSS_2 */ - 0x19, 0x00, /* Teletext Ctl */ -}; - -static const unsigned char init_PAL[] = { - 0x00, 0x71, /* MR0 */ - 0x01, 0x20, /* MR1 */ - 0x02, 0x0e, /* MR2 RTC control: bits 2 and 1 */ - 0x03, 0x80, /* MR3 */ - 0x04, 0x30, /* MR4 */ - 0x05, 0x00, /* Reserved */ - 0x06, 0x00, /* Reserved */ - 0x07, TR0MODE, /* TM0 */ - 0x08, TR1CAPT, /* TM1 */ - 0x09, 0xcb, /* Fsc0 */ - 0x0a, 0x8a, /* Fsc1 */ - 0x0b, 0x09, /* Fsc2 */ - 0x0c, 0x2a, /* Fsc3 */ - 0x0d, 0x00, /* Subcarrier Phase */ - 0x0e, 0x00, /* Closed Capt. Ext 0 */ - 0x0f, 0x00, /* Closed Capt. Ext 1 */ - 0x10, 0x00, /* Closed Capt. 0 */ - 0x11, 0x00, /* Closed Capt. 1 */ - 0x12, 0x00, /* Pedestal Ctl 0 */ - 0x13, 0x00, /* Pedestal Ctl 1 */ - 0x14, 0x00, /* Pedestal Ctl 2 */ - 0x15, 0x00, /* Pedestal Ctl 3 */ - 0x16, 0x00, /* CGMS_WSS_0 */ - 0x17, 0x00, /* CGMS_WSS_1 */ - 0x18, 0x00, /* CGMS_WSS_2 */ - 0x19, 0x00, /* Teletext Ctl */ -}; - - -static int adv7170_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct adv7170 *encoder = to_adv7170(sd); - - v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); - - if (std & V4L2_STD_NTSC) { - adv7170_write_block(sd, init_NTSC, sizeof(init_NTSC)); - if (encoder->input == 0) - adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */ - adv7170_write(sd, 0x07, TR0MODE | TR0RST); - adv7170_write(sd, 0x07, TR0MODE); - } else if (std & V4L2_STD_PAL) { - adv7170_write_block(sd, init_PAL, sizeof(init_PAL)); - if (encoder->input == 0) - adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */ - adv7170_write(sd, 0x07, TR0MODE | TR0RST); - adv7170_write(sd, 0x07, TR0MODE); - } else { - v4l2_dbg(1, debug, sd, "illegal norm: %llx\n", - (unsigned long long)std); - return -EINVAL; - } - v4l2_dbg(1, debug, sd, "switched to %llx\n", (unsigned long long)std); - encoder->norm = std; - return 0; -} - -static int adv7170_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct adv7170 *encoder = to_adv7170(sd); - - /* RJ: input = 0: input is from decoder - input = 1: input is from ZR36060 - input = 2: color bar */ - - v4l2_dbg(1, debug, sd, "set input from %s\n", - input == 0 ? "decoder" : "ZR36060"); - - switch (input) { - case 0: - adv7170_write(sd, 0x01, 0x20); - adv7170_write(sd, 0x08, TR1CAPT); /* TR1 */ - adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */ - adv7170_write(sd, 0x07, TR0MODE | TR0RST); - adv7170_write(sd, 0x07, TR0MODE); - /* udelay(10); */ - break; - - case 1: - adv7170_write(sd, 0x01, 0x00); - adv7170_write(sd, 0x08, TR1PLAY); /* TR1 */ - adv7170_write(sd, 0x02, 0x08); - adv7170_write(sd, 0x07, TR0MODE | TR0RST); - adv7170_write(sd, 0x07, TR0MODE); - /* udelay(10); */ - break; - - default: - v4l2_dbg(1, debug, sd, "illegal input: %d\n", input); - return -EINVAL; - } - v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[input]); - encoder->input = input; - return 0; -} - -static int adv7170_enum_fmt(struct v4l2_subdev *sd, unsigned int index, - enum v4l2_mbus_pixelcode *code) -{ - if (index >= ARRAY_SIZE(adv7170_codes)) - return -EINVAL; - - *code = adv7170_codes[index]; - return 0; -} - -static int adv7170_g_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - u8 val = adv7170_read(sd, 0x7); - - if ((val & 0x40) == (1 << 6)) - mf->code = V4L2_MBUS_FMT_UYVY8_1X16; - else - mf->code = V4L2_MBUS_FMT_UYVY8_2X8; - - mf->colorspace = V4L2_COLORSPACE_SMPTE170M; - mf->width = 0; - mf->height = 0; - mf->field = V4L2_FIELD_ANY; - - return 0; -} - -static int adv7170_s_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - u8 val = adv7170_read(sd, 0x7); - int ret; - - switch (mf->code) { - case V4L2_MBUS_FMT_UYVY8_2X8: - val &= ~0x40; - break; - - case V4L2_MBUS_FMT_UYVY8_1X16: - val |= 0x40; - break; - - default: - v4l2_dbg(1, debug, sd, - "illegal v4l2_mbus_framefmt code: %d\n", mf->code); - return -EINVAL; - } - - ret = adv7170_write(sd, 0x7, val); - - return ret; -} - -static int adv7170_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7170, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops adv7170_core_ops = { - .g_chip_ident = adv7170_g_chip_ident, -}; - -static const struct v4l2_subdev_video_ops adv7170_video_ops = { - .s_std_output = adv7170_s_std_output, - .s_routing = adv7170_s_routing, - .s_mbus_fmt = adv7170_s_fmt, - .g_mbus_fmt = adv7170_g_fmt, - .enum_mbus_fmt = adv7170_enum_fmt, -}; - -static const struct v4l2_subdev_ops adv7170_ops = { - .core = &adv7170_core_ops, - .video = &adv7170_video_ops, -}; - -/* ----------------------------------------------------------------------- */ - -static int adv7170_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct adv7170 *encoder; - struct v4l2_subdev *sd; - int i; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - encoder = kzalloc(sizeof(struct adv7170), GFP_KERNEL); - if (encoder == NULL) - return -ENOMEM; - sd = &encoder->sd; - v4l2_i2c_subdev_init(sd, client, &adv7170_ops); - encoder->norm = V4L2_STD_NTSC; - encoder->input = 0; - - i = adv7170_write_block(sd, init_NTSC, sizeof(init_NTSC)); - if (i >= 0) { - i = adv7170_write(sd, 0x07, TR0MODE | TR0RST); - i = adv7170_write(sd, 0x07, TR0MODE); - i = adv7170_read(sd, 0x12); - v4l2_dbg(1, debug, sd, "revision %d\n", i & 1); - } - if (i < 0) - v4l2_dbg(1, debug, sd, "init error 0x%x\n", i); - return 0; -} - -static int adv7170_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - kfree(to_adv7170(sd)); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id adv7170_id[] = { - { "adv7170", 0 }, - { "adv7171", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, adv7170_id); - -static struct i2c_driver adv7170_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "adv7170", - }, - .probe = adv7170_probe, - .remove = adv7170_remove, - .id_table = adv7170_id, -}; - -module_i2c_driver(adv7170_driver); diff --git a/drivers/media/video/adv7175.c b/drivers/media/video/adv7175.c deleted file mode 100644 index c7640fab573..00000000000 --- a/drivers/media/video/adv7175.c +++ /dev/null @@ -1,460 +0,0 @@ -/* - * adv7175 - adv7175a video encoder driver version 0.0.3 - * - * Copyright (C) 1998 Dave Perks - * Copyright (C) 1999 Wolfgang Scherr - * Copyright (C) 2000 Serguei Miridonov - * - some corrections for Pinnacle Systems Inc. DC10plus card. - * - * Changes by Ronald Bultje - * - moved over to linux>=2.4.x i2c protocol (9/9/2002) - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("Analog Devices ADV7175 video encoder driver"); -MODULE_AUTHOR("Dave Perks"); -MODULE_LICENSE("GPL"); - -#define I2C_ADV7175 0xd4 -#define I2C_ADV7176 0x54 - - -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - -/* ----------------------------------------------------------------------- */ - -struct adv7175 { - struct v4l2_subdev sd; - v4l2_std_id norm; - int input; -}; - -static inline struct adv7175 *to_adv7175(struct v4l2_subdev *sd) -{ - return container_of(sd, struct adv7175, sd); -} - -static char *inputs[] = { "pass_through", "play_back", "color_bar" }; - -static enum v4l2_mbus_pixelcode adv7175_codes[] = { - V4L2_MBUS_FMT_UYVY8_2X8, - V4L2_MBUS_FMT_UYVY8_1X16, -}; - -/* ----------------------------------------------------------------------- */ - -static inline int adv7175_write(struct v4l2_subdev *sd, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_write_byte_data(client, reg, value); -} - -static inline int adv7175_read(struct v4l2_subdev *sd, u8 reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_read_byte_data(client, reg); -} - -static int adv7175_write_block(struct v4l2_subdev *sd, - const u8 *data, unsigned int len) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret = -1; - u8 reg; - - /* the adv7175 has an autoincrement function, use it if - * the adapter understands raw I2C */ - if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - /* do raw I2C, not smbus compatible */ - u8 block_data[32]; - int block_len; - - while (len >= 2) { - block_len = 0; - block_data[block_len++] = reg = data[0]; - do { - block_data[block_len++] = data[1]; - reg++; - len -= 2; - data += 2; - } while (len >= 2 && data[0] == reg && block_len < 32); - ret = i2c_master_send(client, block_data, block_len); - if (ret < 0) - break; - } - } else { - /* do some slow I2C emulation kind of thing */ - while (len >= 2) { - reg = *data++; - ret = adv7175_write(sd, reg, *data++); - if (ret < 0) - break; - len -= 2; - } - } - - return ret; -} - -static void set_subcarrier_freq(struct v4l2_subdev *sd, int pass_through) -{ - /* for some reason pass_through NTSC needs - * a different sub-carrier freq to remain stable. */ - if (pass_through) - adv7175_write(sd, 0x02, 0x00); - else - adv7175_write(sd, 0x02, 0x55); - - adv7175_write(sd, 0x03, 0x55); - adv7175_write(sd, 0x04, 0x55); - adv7175_write(sd, 0x05, 0x25); -} - -/* ----------------------------------------------------------------------- */ -/* Output filter: S-Video Composite */ - -#define MR050 0x11 /* 0x09 */ -#define MR060 0x14 /* 0x0c */ - -/* ----------------------------------------------------------------------- */ - -#define TR0MODE 0x46 -#define TR0RST 0x80 - -#define TR1CAPT 0x80 -#define TR1PLAY 0x00 - -static const unsigned char init_common[] = { - - 0x00, MR050, /* MR0, PAL enabled */ - 0x01, 0x00, /* MR1 */ - 0x02, 0x0c, /* subc. freq. */ - 0x03, 0x8c, /* subc. freq. */ - 0x04, 0x79, /* subc. freq. */ - 0x05, 0x26, /* subc. freq. */ - 0x06, 0x40, /* subc. phase */ - - 0x07, TR0MODE, /* TR0, 16bit */ - 0x08, 0x21, /* */ - 0x09, 0x00, /* */ - 0x0a, 0x00, /* */ - 0x0b, 0x00, /* */ - 0x0c, TR1CAPT, /* TR1 */ - 0x0d, 0x4f, /* MR2 */ - 0x0e, 0x00, /* */ - 0x0f, 0x00, /* */ - 0x10, 0x00, /* */ - 0x11, 0x00, /* */ -}; - -static const unsigned char init_pal[] = { - 0x00, MR050, /* MR0, PAL enabled */ - 0x01, 0x00, /* MR1 */ - 0x02, 0x0c, /* subc. freq. */ - 0x03, 0x8c, /* subc. freq. */ - 0x04, 0x79, /* subc. freq. */ - 0x05, 0x26, /* subc. freq. */ - 0x06, 0x40, /* subc. phase */ -}; - -static const unsigned char init_ntsc[] = { - 0x00, MR060, /* MR0, NTSC enabled */ - 0x01, 0x00, /* MR1 */ - 0x02, 0x55, /* subc. freq. */ - 0x03, 0x55, /* subc. freq. */ - 0x04, 0x55, /* subc. freq. */ - 0x05, 0x25, /* subc. freq. */ - 0x06, 0x1a, /* subc. phase */ -}; - -static int adv7175_init(struct v4l2_subdev *sd, u32 val) -{ - /* This is just for testing!!! */ - adv7175_write_block(sd, init_common, sizeof(init_common)); - adv7175_write(sd, 0x07, TR0MODE | TR0RST); - adv7175_write(sd, 0x07, TR0MODE); - return 0; -} - -static int adv7175_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct adv7175 *encoder = to_adv7175(sd); - - if (std & V4L2_STD_NTSC) { - adv7175_write_block(sd, init_ntsc, sizeof(init_ntsc)); - if (encoder->input == 0) - adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */ - adv7175_write(sd, 0x07, TR0MODE | TR0RST); - adv7175_write(sd, 0x07, TR0MODE); - } else if (std & V4L2_STD_PAL) { - adv7175_write_block(sd, init_pal, sizeof(init_pal)); - if (encoder->input == 0) - adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */ - adv7175_write(sd, 0x07, TR0MODE | TR0RST); - adv7175_write(sd, 0x07, TR0MODE); - } else if (std & V4L2_STD_SECAM) { - /* This is an attempt to convert - * SECAM->PAL (typically it does not work - * due to genlock: when decoder is in SECAM - * and encoder in in PAL the subcarrier can - * not be syncronized with horizontal - * quency) */ - adv7175_write_block(sd, init_pal, sizeof(init_pal)); - if (encoder->input == 0) - adv7175_write(sd, 0x0d, 0x49); /* Disable genlock */ - adv7175_write(sd, 0x07, TR0MODE | TR0RST); - adv7175_write(sd, 0x07, TR0MODE); - } else { - v4l2_dbg(1, debug, sd, "illegal norm: %llx\n", - (unsigned long long)std); - return -EINVAL; - } - v4l2_dbg(1, debug, sd, "switched to %llx\n", (unsigned long long)std); - encoder->norm = std; - return 0; -} - -static int adv7175_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct adv7175 *encoder = to_adv7175(sd); - - /* RJ: input = 0: input is from decoder - input = 1: input is from ZR36060 - input = 2: color bar */ - - switch (input) { - case 0: - adv7175_write(sd, 0x01, 0x00); - - if (encoder->norm & V4L2_STD_NTSC) - set_subcarrier_freq(sd, 1); - - adv7175_write(sd, 0x0c, TR1CAPT); /* TR1 */ - if (encoder->norm & V4L2_STD_SECAM) - adv7175_write(sd, 0x0d, 0x49); /* Disable genlock */ - else - adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */ - adv7175_write(sd, 0x07, TR0MODE | TR0RST); - adv7175_write(sd, 0x07, TR0MODE); - /*udelay(10);*/ - break; - - case 1: - adv7175_write(sd, 0x01, 0x00); - - if (encoder->norm & V4L2_STD_NTSC) - set_subcarrier_freq(sd, 0); - - adv7175_write(sd, 0x0c, TR1PLAY); /* TR1 */ - adv7175_write(sd, 0x0d, 0x49); - adv7175_write(sd, 0x07, TR0MODE | TR0RST); - adv7175_write(sd, 0x07, TR0MODE); - /* udelay(10); */ - break; - - case 2: - adv7175_write(sd, 0x01, 0x80); - - if (encoder->norm & V4L2_STD_NTSC) - set_subcarrier_freq(sd, 0); - - adv7175_write(sd, 0x0d, 0x49); - adv7175_write(sd, 0x07, TR0MODE | TR0RST); - adv7175_write(sd, 0x07, TR0MODE); - /* udelay(10); */ - break; - - default: - v4l2_dbg(1, debug, sd, "illegal input: %d\n", input); - return -EINVAL; - } - v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[input]); - encoder->input = input; - return 0; -} - -static int adv7175_enum_fmt(struct v4l2_subdev *sd, unsigned int index, - enum v4l2_mbus_pixelcode *code) -{ - if (index >= ARRAY_SIZE(adv7175_codes)) - return -EINVAL; - - *code = adv7175_codes[index]; - return 0; -} - -static int adv7175_g_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - u8 val = adv7175_read(sd, 0x7); - - if ((val & 0x40) == (1 << 6)) - mf->code = V4L2_MBUS_FMT_UYVY8_1X16; - else - mf->code = V4L2_MBUS_FMT_UYVY8_2X8; - - mf->colorspace = V4L2_COLORSPACE_SMPTE170M; - mf->width = 0; - mf->height = 0; - mf->field = V4L2_FIELD_ANY; - - return 0; -} - -static int adv7175_s_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - u8 val = adv7175_read(sd, 0x7); - int ret; - - switch (mf->code) { - case V4L2_MBUS_FMT_UYVY8_2X8: - val &= ~0x40; - break; - - case V4L2_MBUS_FMT_UYVY8_1X16: - val |= 0x40; - break; - - default: - v4l2_dbg(1, debug, sd, - "illegal v4l2_mbus_framefmt code: %d\n", mf->code); - return -EINVAL; - } - - ret = adv7175_write(sd, 0x7, val); - - return ret; -} - -static int adv7175_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7175, 0); -} - -static int adv7175_s_power(struct v4l2_subdev *sd, int on) -{ - if (on) - adv7175_write(sd, 0x01, 0x00); - else - adv7175_write(sd, 0x01, 0x78); - - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops adv7175_core_ops = { - .g_chip_ident = adv7175_g_chip_ident, - .init = adv7175_init, - .s_power = adv7175_s_power, -}; - -static const struct v4l2_subdev_video_ops adv7175_video_ops = { - .s_std_output = adv7175_s_std_output, - .s_routing = adv7175_s_routing, - .s_mbus_fmt = adv7175_s_fmt, - .g_mbus_fmt = adv7175_g_fmt, - .enum_mbus_fmt = adv7175_enum_fmt, -}; - -static const struct v4l2_subdev_ops adv7175_ops = { - .core = &adv7175_core_ops, - .video = &adv7175_video_ops, -}; - -/* ----------------------------------------------------------------------- */ - -static int adv7175_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - int i; - struct adv7175 *encoder; - struct v4l2_subdev *sd; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - encoder = kzalloc(sizeof(struct adv7175), GFP_KERNEL); - if (encoder == NULL) - return -ENOMEM; - sd = &encoder->sd; - v4l2_i2c_subdev_init(sd, client, &adv7175_ops); - encoder->norm = V4L2_STD_NTSC; - encoder->input = 0; - - i = adv7175_write_block(sd, init_common, sizeof(init_common)); - if (i >= 0) { - i = adv7175_write(sd, 0x07, TR0MODE | TR0RST); - i = adv7175_write(sd, 0x07, TR0MODE); - i = adv7175_read(sd, 0x12); - v4l2_dbg(1, debug, sd, "revision %d\n", i & 1); - } - if (i < 0) - v4l2_dbg(1, debug, sd, "init error 0x%x\n", i); - return 0; -} - -static int adv7175_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - kfree(to_adv7175(sd)); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id adv7175_id[] = { - { "adv7175", 0 }, - { "adv7176", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, adv7175_id); - -static struct i2c_driver adv7175_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "adv7175", - }, - .probe = adv7175_probe, - .remove = adv7175_remove, - .id_table = adv7175_id, -}; - -module_i2c_driver(adv7175_driver); diff --git a/drivers/media/video/adv7180.c b/drivers/media/video/adv7180.c deleted file mode 100644 index 45ecf8db1ea..00000000000 --- a/drivers/media/video/adv7180.c +++ /dev/null @@ -1,667 +0,0 @@ -/* - * adv7180.c Analog Devices ADV7180 video decoder driver - * Copyright (c) 2009 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define ADV7180_INPUT_CONTROL_REG 0x00 -#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM 0x00 -#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM_PED 0x10 -#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_J_SECAM 0x20 -#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_M_SECAM 0x30 -#define ADV7180_INPUT_CONTROL_NTSC_J 0x40 -#define ADV7180_INPUT_CONTROL_NTSC_M 0x50 -#define ADV7180_INPUT_CONTROL_PAL60 0x60 -#define ADV7180_INPUT_CONTROL_NTSC_443 0x70 -#define ADV7180_INPUT_CONTROL_PAL_BG 0x80 -#define ADV7180_INPUT_CONTROL_PAL_N 0x90 -#define ADV7180_INPUT_CONTROL_PAL_M 0xa0 -#define ADV7180_INPUT_CONTROL_PAL_M_PED 0xb0 -#define ADV7180_INPUT_CONTROL_PAL_COMB_N 0xc0 -#define ADV7180_INPUT_CONTROL_PAL_COMB_N_PED 0xd0 -#define ADV7180_INPUT_CONTROL_PAL_SECAM 0xe0 -#define ADV7180_INPUT_CONTROL_PAL_SECAM_PED 0xf0 -#define ADV7180_INPUT_CONTROL_INSEL_MASK 0x0f - -#define ADV7180_EXTENDED_OUTPUT_CONTROL_REG 0x04 -#define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS 0xC5 - -#define ADV7180_AUTODETECT_ENABLE_REG 0x07 -#define ADV7180_AUTODETECT_DEFAULT 0x7f -/* Contrast */ -#define ADV7180_CON_REG 0x08 /*Unsigned */ -#define ADV7180_CON_MIN 0 -#define ADV7180_CON_DEF 128 -#define ADV7180_CON_MAX 255 -/* Brightness*/ -#define ADV7180_BRI_REG 0x0a /*Signed */ -#define ADV7180_BRI_MIN -128 -#define ADV7180_BRI_DEF 0 -#define ADV7180_BRI_MAX 127 -/* Hue */ -#define ADV7180_HUE_REG 0x0b /*Signed, inverted */ -#define ADV7180_HUE_MIN -127 -#define ADV7180_HUE_DEF 0 -#define ADV7180_HUE_MAX 128 - -#define ADV7180_ADI_CTRL_REG 0x0e -#define ADV7180_ADI_CTRL_IRQ_SPACE 0x20 - -#define ADV7180_PWR_MAN_REG 0x0f -#define ADV7180_PWR_MAN_ON 0x04 -#define ADV7180_PWR_MAN_OFF 0x24 -#define ADV7180_PWR_MAN_RES 0x80 - -#define ADV7180_STATUS1_REG 0x10 -#define ADV7180_STATUS1_IN_LOCK 0x01 -#define ADV7180_STATUS1_AUTOD_MASK 0x70 -#define ADV7180_STATUS1_AUTOD_NTSM_M_J 0x00 -#define ADV7180_STATUS1_AUTOD_NTSC_4_43 0x10 -#define ADV7180_STATUS1_AUTOD_PAL_M 0x20 -#define ADV7180_STATUS1_AUTOD_PAL_60 0x30 -#define ADV7180_STATUS1_AUTOD_PAL_B_G 0x40 -#define ADV7180_STATUS1_AUTOD_SECAM 0x50 -#define ADV7180_STATUS1_AUTOD_PAL_COMB 0x60 -#define ADV7180_STATUS1_AUTOD_SECAM_525 0x70 - -#define ADV7180_IDENT_REG 0x11 -#define ADV7180_ID_7180 0x18 - -#define ADV7180_ICONF1_ADI 0x40 -#define ADV7180_ICONF1_ACTIVE_LOW 0x01 -#define ADV7180_ICONF1_PSYNC_ONLY 0x10 -#define ADV7180_ICONF1_ACTIVE_TO_CLR 0xC0 -/* Saturation */ -#define ADV7180_SD_SAT_CB_REG 0xe3 /*Unsigned */ -#define ADV7180_SD_SAT_CR_REG 0xe4 /*Unsigned */ -#define ADV7180_SAT_MIN 0 -#define ADV7180_SAT_DEF 128 -#define ADV7180_SAT_MAX 255 - -#define ADV7180_IRQ1_LOCK 0x01 -#define ADV7180_IRQ1_UNLOCK 0x02 -#define ADV7180_ISR1_ADI 0x42 -#define ADV7180_ICR1_ADI 0x43 -#define ADV7180_IMR1_ADI 0x44 -#define ADV7180_IMR2_ADI 0x48 -#define ADV7180_IRQ3_AD_CHANGE 0x08 -#define ADV7180_ISR3_ADI 0x4A -#define ADV7180_ICR3_ADI 0x4B -#define ADV7180_IMR3_ADI 0x4C -#define ADV7180_IMR4_ADI 0x50 - -#define ADV7180_NTSC_V_BIT_END_REG 0xE6 -#define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND 0x4F - -struct adv7180_state { - struct v4l2_ctrl_handler ctrl_hdl; - struct v4l2_subdev sd; - struct work_struct work; - struct mutex mutex; /* mutual excl. when accessing chip */ - int irq; - v4l2_std_id curr_norm; - bool autodetect; - u8 input; -}; -#define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler, \ - struct adv7180_state, \ - ctrl_hdl)->sd) - -static v4l2_std_id adv7180_std_to_v4l2(u8 status1) -{ - switch (status1 & ADV7180_STATUS1_AUTOD_MASK) { - case ADV7180_STATUS1_AUTOD_NTSM_M_J: - return V4L2_STD_NTSC; - case ADV7180_STATUS1_AUTOD_NTSC_4_43: - return V4L2_STD_NTSC_443; - case ADV7180_STATUS1_AUTOD_PAL_M: - return V4L2_STD_PAL_M; - case ADV7180_STATUS1_AUTOD_PAL_60: - return V4L2_STD_PAL_60; - case ADV7180_STATUS1_AUTOD_PAL_B_G: - return V4L2_STD_PAL; - case ADV7180_STATUS1_AUTOD_SECAM: - return V4L2_STD_SECAM; - case ADV7180_STATUS1_AUTOD_PAL_COMB: - return V4L2_STD_PAL_Nc | V4L2_STD_PAL_N; - case ADV7180_STATUS1_AUTOD_SECAM_525: - return V4L2_STD_SECAM; - default: - return V4L2_STD_UNKNOWN; - } -} - -static int v4l2_std_to_adv7180(v4l2_std_id std) -{ - if (std == V4L2_STD_PAL_60) - return ADV7180_INPUT_CONTROL_PAL60; - if (std == V4L2_STD_NTSC_443) - return ADV7180_INPUT_CONTROL_NTSC_443; - if (std == V4L2_STD_PAL_N) - return ADV7180_INPUT_CONTROL_PAL_N; - if (std == V4L2_STD_PAL_M) - return ADV7180_INPUT_CONTROL_PAL_M; - if (std == V4L2_STD_PAL_Nc) - return ADV7180_INPUT_CONTROL_PAL_COMB_N; - - if (std & V4L2_STD_PAL) - return ADV7180_INPUT_CONTROL_PAL_BG; - if (std & V4L2_STD_NTSC) - return ADV7180_INPUT_CONTROL_NTSC_M; - if (std & V4L2_STD_SECAM) - return ADV7180_INPUT_CONTROL_PAL_SECAM; - - return -EINVAL; -} - -static u32 adv7180_status_to_v4l2(u8 status1) -{ - if (!(status1 & ADV7180_STATUS1_IN_LOCK)) - return V4L2_IN_ST_NO_SIGNAL; - - return 0; -} - -static int __adv7180_status(struct i2c_client *client, u32 *status, - v4l2_std_id *std) -{ - int status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG); - - if (status1 < 0) - return status1; - - if (status) - *status = adv7180_status_to_v4l2(status1); - if (std) - *std = adv7180_std_to_v4l2(status1); - - return 0; -} - -static inline struct adv7180_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct adv7180_state, sd); -} - -static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) -{ - struct adv7180_state *state = to_state(sd); - int err = mutex_lock_interruptible(&state->mutex); - if (err) - return err; - - /* when we are interrupt driven we know the state */ - if (!state->autodetect || state->irq > 0) - *std = state->curr_norm; - else - err = __adv7180_status(v4l2_get_subdevdata(sd), NULL, std); - - mutex_unlock(&state->mutex); - return err; -} - -static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input, - u32 output, u32 config) -{ - struct adv7180_state *state = to_state(sd); - int ret = mutex_lock_interruptible(&state->mutex); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (ret) - return ret; - - /* We cannot discriminate between LQFP and 40-pin LFCSP, so accept - * all inputs and let the card driver take care of validation - */ - if ((input & ADV7180_INPUT_CONTROL_INSEL_MASK) != input) - goto out; - - ret = i2c_smbus_read_byte_data(client, ADV7180_INPUT_CONTROL_REG); - - if (ret < 0) - goto out; - - ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK; - ret = i2c_smbus_write_byte_data(client, - ADV7180_INPUT_CONTROL_REG, ret | input); - state->input = input; -out: - mutex_unlock(&state->mutex); - return ret; -} - -static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status) -{ - struct adv7180_state *state = to_state(sd); - int ret = mutex_lock_interruptible(&state->mutex); - if (ret) - return ret; - - ret = __adv7180_status(v4l2_get_subdevdata(sd), status, NULL); - mutex_unlock(&state->mutex); - return ret; -} - -static int adv7180_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7180, 0); -} - -static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct adv7180_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret = mutex_lock_interruptible(&state->mutex); - if (ret) - return ret; - - /* all standards -> autodetect */ - if (std == V4L2_STD_ALL) { - ret = - i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, - ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM - | state->input); - if (ret < 0) - goto out; - - __adv7180_status(client, NULL, &state->curr_norm); - state->autodetect = true; - } else { - ret = v4l2_std_to_adv7180(std); - if (ret < 0) - goto out; - - ret = i2c_smbus_write_byte_data(client, - ADV7180_INPUT_CONTROL_REG, - ret | state->input); - if (ret < 0) - goto out; - - state->curr_norm = std; - state->autodetect = false; - } - ret = 0; -out: - mutex_unlock(&state->mutex); - return ret; -} - -static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_adv7180_sd(ctrl); - struct adv7180_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret = mutex_lock_interruptible(&state->mutex); - int val; - - if (ret) - return ret; - val = ctrl->val; - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ret = i2c_smbus_write_byte_data(client, ADV7180_BRI_REG, val); - break; - case V4L2_CID_HUE: - /*Hue is inverted according to HSL chart */ - ret = i2c_smbus_write_byte_data(client, ADV7180_HUE_REG, -val); - break; - case V4L2_CID_CONTRAST: - ret = i2c_smbus_write_byte_data(client, ADV7180_CON_REG, val); - break; - case V4L2_CID_SATURATION: - /* - *This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE - *Let's not confuse the user, everybody understands saturation - */ - ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CB_REG, - val); - if (ret < 0) - break; - ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CR_REG, - val); - break; - default: - ret = -EINVAL; - } - - mutex_unlock(&state->mutex); - return ret; -} - -static const struct v4l2_ctrl_ops adv7180_ctrl_ops = { - .s_ctrl = adv7180_s_ctrl, -}; - -static int adv7180_init_controls(struct adv7180_state *state) -{ - v4l2_ctrl_handler_init(&state->ctrl_hdl, 4); - - v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, - V4L2_CID_BRIGHTNESS, ADV7180_BRI_MIN, - ADV7180_BRI_MAX, 1, ADV7180_BRI_DEF); - v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, - V4L2_CID_CONTRAST, ADV7180_CON_MIN, - ADV7180_CON_MAX, 1, ADV7180_CON_DEF); - v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, - V4L2_CID_SATURATION, ADV7180_SAT_MIN, - ADV7180_SAT_MAX, 1, ADV7180_SAT_DEF); - v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, - V4L2_CID_HUE, ADV7180_HUE_MIN, - ADV7180_HUE_MAX, 1, ADV7180_HUE_DEF); - state->sd.ctrl_handler = &state->ctrl_hdl; - if (state->ctrl_hdl.error) { - int err = state->ctrl_hdl.error; - - v4l2_ctrl_handler_free(&state->ctrl_hdl); - return err; - } - v4l2_ctrl_handler_setup(&state->ctrl_hdl); - - return 0; -} -static void adv7180_exit_controls(struct adv7180_state *state) -{ - v4l2_ctrl_handler_free(&state->ctrl_hdl); -} - -static const struct v4l2_subdev_video_ops adv7180_video_ops = { - .querystd = adv7180_querystd, - .g_input_status = adv7180_g_input_status, - .s_routing = adv7180_s_routing, -}; - -static const struct v4l2_subdev_core_ops adv7180_core_ops = { - .g_chip_ident = adv7180_g_chip_ident, - .s_std = adv7180_s_std, - .queryctrl = v4l2_subdev_queryctrl, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, -}; - -static const struct v4l2_subdev_ops adv7180_ops = { - .core = &adv7180_core_ops, - .video = &adv7180_video_ops, -}; - -static void adv7180_work(struct work_struct *work) -{ - struct adv7180_state *state = container_of(work, struct adv7180_state, - work); - struct i2c_client *client = v4l2_get_subdevdata(&state->sd); - u8 isr3; - - mutex_lock(&state->mutex); - i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, - ADV7180_ADI_CTRL_IRQ_SPACE); - isr3 = i2c_smbus_read_byte_data(client, ADV7180_ISR3_ADI); - /* clear */ - i2c_smbus_write_byte_data(client, ADV7180_ICR3_ADI, isr3); - i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, 0); - - if (isr3 & ADV7180_IRQ3_AD_CHANGE && state->autodetect) - __adv7180_status(client, NULL, &state->curr_norm); - mutex_unlock(&state->mutex); - - enable_irq(state->irq); -} - -static irqreturn_t adv7180_irq(int irq, void *devid) -{ - struct adv7180_state *state = devid; - - schedule_work(&state->work); - - disable_irq_nosync(state->irq); - - return IRQ_HANDLED; -} - -static int init_device(struct i2c_client *client, struct adv7180_state *state) -{ - int ret; - - /* Initialize adv7180 */ - /* Enable autodetection */ - if (state->autodetect) { - ret = - i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, - ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM - | state->input); - if (ret < 0) - return ret; - - ret = - i2c_smbus_write_byte_data(client, - ADV7180_AUTODETECT_ENABLE_REG, - ADV7180_AUTODETECT_DEFAULT); - if (ret < 0) - return ret; - } else { - ret = v4l2_std_to_adv7180(state->curr_norm); - if (ret < 0) - return ret; - - ret = - i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, - ret | state->input); - if (ret < 0) - return ret; - - } - /* ITU-R BT.656-4 compatible */ - ret = i2c_smbus_write_byte_data(client, - ADV7180_EXTENDED_OUTPUT_CONTROL_REG, - ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS); - if (ret < 0) - return ret; - - /* Manually set V bit end position in NTSC mode */ - ret = i2c_smbus_write_byte_data(client, - ADV7180_NTSC_V_BIT_END_REG, - ADV7180_NTSC_V_BIT_END_MANUAL_NVEND); - if (ret < 0) - return ret; - - /* read current norm */ - __adv7180_status(client, NULL, &state->curr_norm); - - /* register for interrupts */ - if (state->irq > 0) { - ret = request_irq(state->irq, adv7180_irq, 0, KBUILD_MODNAME, - state); - if (ret) - return ret; - - ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, - ADV7180_ADI_CTRL_IRQ_SPACE); - if (ret < 0) - return ret; - - /* config the Interrupt pin to be active low */ - ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI, - ADV7180_ICONF1_ACTIVE_LOW | - ADV7180_ICONF1_PSYNC_ONLY); - if (ret < 0) - return ret; - - ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0); - if (ret < 0) - return ret; - - ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0); - if (ret < 0) - return ret; - - /* enable AD change interrupts interrupts */ - ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI, - ADV7180_IRQ3_AD_CHANGE); - if (ret < 0) - return ret; - - ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0); - if (ret < 0) - return ret; - - ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, - 0); - if (ret < 0) - return ret; - } - - return 0; -} - -static __devinit int adv7180_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct adv7180_state *state; - struct v4l2_subdev *sd; - int ret; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - v4l_info(client, "chip found @ 0x%02x (%s)\n", - client->addr, client->adapter->name); - - state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL); - if (state == NULL) { - ret = -ENOMEM; - goto err; - } - - state->irq = client->irq; - INIT_WORK(&state->work, adv7180_work); - mutex_init(&state->mutex); - state->autodetect = true; - state->input = 0; - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &adv7180_ops); - - ret = adv7180_init_controls(state); - if (ret) - goto err_unreg_subdev; - ret = init_device(client, state); - if (ret) - goto err_free_ctrl; - return 0; - -err_free_ctrl: - adv7180_exit_controls(state); -err_unreg_subdev: - mutex_destroy(&state->mutex); - v4l2_device_unregister_subdev(sd); - kfree(state); -err: - printk(KERN_ERR KBUILD_MODNAME ": Failed to probe: %d\n", ret); - return ret; -} - -static __devexit int adv7180_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct adv7180_state *state = to_state(sd); - - if (state->irq > 0) { - free_irq(client->irq, state); - if (cancel_work_sync(&state->work)) { - /* - * Work was pending, therefore we need to enable - * IRQ here to balance the disable_irq() done in the - * interrupt handler. - */ - enable_irq(state->irq); - } - } - - mutex_destroy(&state->mutex); - v4l2_device_unregister_subdev(sd); - kfree(to_state(sd)); - return 0; -} - -static const struct i2c_device_id adv7180_id[] = { - {KBUILD_MODNAME, 0}, - {}, -}; - -#ifdef CONFIG_PM -static int adv7180_suspend(struct i2c_client *client, pm_message_t state) -{ - int ret; - - ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, - ADV7180_PWR_MAN_OFF); - if (ret < 0) - return ret; - return 0; -} - -static int adv7180_resume(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct adv7180_state *state = to_state(sd); - int ret; - - ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, - ADV7180_PWR_MAN_ON); - if (ret < 0) - return ret; - ret = init_device(client, state); - if (ret < 0) - return ret; - return 0; -} -#endif - -MODULE_DEVICE_TABLE(i2c, adv7180_id); - -static struct i2c_driver adv7180_driver = { - .driver = { - .owner = THIS_MODULE, - .name = KBUILD_MODNAME, - }, - .probe = adv7180_probe, - .remove = __devexit_p(adv7180_remove), -#ifdef CONFIG_PM - .suspend = adv7180_suspend, - .resume = adv7180_resume, -#endif - .id_table = adv7180_id, -}; - -module_i2c_driver(adv7180_driver); - -MODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver"); -MODULE_AUTHOR("Mocean Laboratories"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/adv7183.c b/drivers/media/video/adv7183.c deleted file mode 100644 index e1d4c89d714..00000000000 --- a/drivers/media/video/adv7183.c +++ /dev/null @@ -1,699 +0,0 @@ -/* - * adv7183.c Analog Devices ADV7183 video decoder driver - * - * Copyright (c) 2011 Analog Devices Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "adv7183_regs.h" - -struct adv7183 { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - - v4l2_std_id std; /* Current set standard */ - u32 input; - u32 output; - unsigned reset_pin; - unsigned oe_pin; - struct v4l2_mbus_framefmt fmt; -}; - -/* EXAMPLES USING 27 MHz CLOCK - * Mode 1 CVBS Input (Composite Video on AIN5) - * All standards are supported through autodetect, 8-bit, 4:2:2, ITU-R BT.656 output on P15 to P8. - */ -static const unsigned char adv7183_init_regs[] = { - ADV7183_IN_CTRL, 0x04, /* CVBS input on AIN5 */ - ADV7183_DIGI_CLAMP_CTRL_1, 0x00, /* Slow down digital clamps */ - ADV7183_SHAP_FILT_CTRL, 0x41, /* Set CSFM to SH1 */ - ADV7183_ADC_CTRL, 0x16, /* Power down ADC 1 and ADC 2 */ - ADV7183_CTI_DNR_CTRL_4, 0x04, /* Set DNR threshold to 4 for flat response */ - /* ADI recommended programming sequence */ - ADV7183_ADI_CTRL, 0x80, - ADV7183_CTI_DNR_CTRL_4, 0x20, - 0x52, 0x18, - 0x58, 0xED, - 0x77, 0xC5, - 0x7C, 0x93, - 0x7D, 0x00, - 0xD0, 0x48, - 0xD5, 0xA0, - 0xD7, 0xEA, - ADV7183_SD_SATURATION_CR, 0x3E, - ADV7183_PAL_V_END, 0x3E, - ADV7183_PAL_F_TOGGLE, 0x0F, - ADV7183_ADI_CTRL, 0x00, -}; - -static inline struct adv7183 *to_adv7183(struct v4l2_subdev *sd) -{ - return container_of(sd, struct adv7183, sd); -} -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct adv7183, hdl)->sd; -} - -static inline int adv7183_read(struct v4l2_subdev *sd, unsigned char reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_read_byte_data(client, reg); -} - -static inline int adv7183_write(struct v4l2_subdev *sd, unsigned char reg, - unsigned char value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_write_byte_data(client, reg, value); -} - -static int adv7183_writeregs(struct v4l2_subdev *sd, - const unsigned char *regs, unsigned int num) -{ - unsigned char reg, data; - unsigned int cnt = 0; - - if (num & 0x1) { - v4l2_err(sd, "invalid regs array\n"); - return -1; - } - - while (cnt < num) { - reg = *regs++; - data = *regs++; - cnt += 2; - - adv7183_write(sd, reg, data); - } - return 0; -} - -static int adv7183_log_status(struct v4l2_subdev *sd) -{ - struct adv7183 *decoder = to_adv7183(sd); - - v4l2_info(sd, "adv7183: Input control = 0x%02x\n", - adv7183_read(sd, ADV7183_IN_CTRL)); - v4l2_info(sd, "adv7183: Video selection = 0x%02x\n", - adv7183_read(sd, ADV7183_VD_SEL)); - v4l2_info(sd, "adv7183: Output control = 0x%02x\n", - adv7183_read(sd, ADV7183_OUT_CTRL)); - v4l2_info(sd, "adv7183: Extended output control = 0x%02x\n", - adv7183_read(sd, ADV7183_EXT_OUT_CTRL)); - v4l2_info(sd, "adv7183: Autodetect enable = 0x%02x\n", - adv7183_read(sd, ADV7183_AUTO_DET_EN)); - v4l2_info(sd, "adv7183: Contrast = 0x%02x\n", - adv7183_read(sd, ADV7183_CONTRAST)); - v4l2_info(sd, "adv7183: Brightness = 0x%02x\n", - adv7183_read(sd, ADV7183_BRIGHTNESS)); - v4l2_info(sd, "adv7183: Hue = 0x%02x\n", - adv7183_read(sd, ADV7183_HUE)); - v4l2_info(sd, "adv7183: Default value Y = 0x%02x\n", - adv7183_read(sd, ADV7183_DEF_Y)); - v4l2_info(sd, "adv7183: Default value C = 0x%02x\n", - adv7183_read(sd, ADV7183_DEF_C)); - v4l2_info(sd, "adv7183: ADI control = 0x%02x\n", - adv7183_read(sd, ADV7183_ADI_CTRL)); - v4l2_info(sd, "adv7183: Power Management = 0x%02x\n", - adv7183_read(sd, ADV7183_POW_MANAGE)); - v4l2_info(sd, "adv7183: Status 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n", - adv7183_read(sd, ADV7183_STATUS_1), - adv7183_read(sd, ADV7183_STATUS_2), - adv7183_read(sd, ADV7183_STATUS_3)); - v4l2_info(sd, "adv7183: Ident = 0x%02x\n", - adv7183_read(sd, ADV7183_IDENT)); - v4l2_info(sd, "adv7183: Analog clamp control = 0x%02x\n", - adv7183_read(sd, ADV7183_ANAL_CLAMP_CTRL)); - v4l2_info(sd, "adv7183: Digital clamp control 1 = 0x%02x\n", - adv7183_read(sd, ADV7183_DIGI_CLAMP_CTRL_1)); - v4l2_info(sd, "adv7183: Shaping filter control 1 and 2 = 0x%02x 0x%02x\n", - adv7183_read(sd, ADV7183_SHAP_FILT_CTRL), - adv7183_read(sd, ADV7183_SHAP_FILT_CTRL_2)); - v4l2_info(sd, "adv7183: Comb filter control = 0x%02x\n", - adv7183_read(sd, ADV7183_COMB_FILT_CTRL)); - v4l2_info(sd, "adv7183: ADI control 2 = 0x%02x\n", - adv7183_read(sd, ADV7183_ADI_CTRL_2)); - v4l2_info(sd, "adv7183: Pixel delay control = 0x%02x\n", - adv7183_read(sd, ADV7183_PIX_DELAY_CTRL)); - v4l2_info(sd, "adv7183: Misc gain control = 0x%02x\n", - adv7183_read(sd, ADV7183_MISC_GAIN_CTRL)); - v4l2_info(sd, "adv7183: AGC mode control = 0x%02x\n", - adv7183_read(sd, ADV7183_AGC_MODE_CTRL)); - v4l2_info(sd, "adv7183: Chroma gain control 1 and 2 = 0x%02x 0x%02x\n", - adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_1), - adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_2)); - v4l2_info(sd, "adv7183: Luma gain control 1 and 2 = 0x%02x 0x%02x\n", - adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_1), - adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_2)); - v4l2_info(sd, "adv7183: Vsync field control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n", - adv7183_read(sd, ADV7183_VS_FIELD_CTRL_1), - adv7183_read(sd, ADV7183_VS_FIELD_CTRL_2), - adv7183_read(sd, ADV7183_VS_FIELD_CTRL_3)); - v4l2_info(sd, "adv7183: Hsync positon control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n", - adv7183_read(sd, ADV7183_HS_POS_CTRL_1), - adv7183_read(sd, ADV7183_HS_POS_CTRL_2), - adv7183_read(sd, ADV7183_HS_POS_CTRL_3)); - v4l2_info(sd, "adv7183: Polarity = 0x%02x\n", - adv7183_read(sd, ADV7183_POLARITY)); - v4l2_info(sd, "adv7183: ADC control = 0x%02x\n", - adv7183_read(sd, ADV7183_ADC_CTRL)); - v4l2_info(sd, "adv7183: SD offset Cb and Cr = 0x%02x 0x%02x\n", - adv7183_read(sd, ADV7183_SD_OFFSET_CB), - adv7183_read(sd, ADV7183_SD_OFFSET_CR)); - v4l2_info(sd, "adv7183: SD saturation Cb and Cr = 0x%02x 0x%02x\n", - adv7183_read(sd, ADV7183_SD_SATURATION_CB), - adv7183_read(sd, ADV7183_SD_SATURATION_CR)); - v4l2_info(sd, "adv7183: Drive strength = 0x%02x\n", - adv7183_read(sd, ADV7183_DRIVE_STR)); - v4l2_ctrl_handler_log_status(&decoder->hdl, sd->name); - return 0; -} - -static int adv7183_g_std(struct v4l2_subdev *sd, v4l2_std_id *std) -{ - struct adv7183 *decoder = to_adv7183(sd); - - *std = decoder->std; - return 0; -} - -static int adv7183_s_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct adv7183 *decoder = to_adv7183(sd); - int reg; - - reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF; - if (std == V4L2_STD_PAL_60) - reg |= 0x60; - else if (std == V4L2_STD_NTSC_443) - reg |= 0x70; - else if (std == V4L2_STD_PAL_N) - reg |= 0x90; - else if (std == V4L2_STD_PAL_M) - reg |= 0xA0; - else if (std == V4L2_STD_PAL_Nc) - reg |= 0xC0; - else if (std & V4L2_STD_PAL) - reg |= 0x80; - else if (std & V4L2_STD_NTSC) - reg |= 0x50; - else if (std & V4L2_STD_SECAM) - reg |= 0xE0; - else - return -EINVAL; - adv7183_write(sd, ADV7183_IN_CTRL, reg); - - decoder->std = std; - - return 0; -} - -static int adv7183_reset(struct v4l2_subdev *sd, u32 val) -{ - int reg; - - reg = adv7183_read(sd, ADV7183_POW_MANAGE) | 0x80; - adv7183_write(sd, ADV7183_POW_MANAGE, reg); - /* wait 5ms before any further i2c writes are performed */ - usleep_range(5000, 10000); - return 0; -} - -static int adv7183_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct adv7183 *decoder = to_adv7183(sd); - int reg; - - if ((input > ADV7183_COMPONENT1) || (output > ADV7183_16BIT_OUT)) - return -EINVAL; - - if (input != decoder->input) { - decoder->input = input; - reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF0; - switch (input) { - case ADV7183_COMPOSITE1: - reg |= 0x1; - break; - case ADV7183_COMPOSITE2: - reg |= 0x2; - break; - case ADV7183_COMPOSITE3: - reg |= 0x3; - break; - case ADV7183_COMPOSITE4: - reg |= 0x4; - break; - case ADV7183_COMPOSITE5: - reg |= 0x5; - break; - case ADV7183_COMPOSITE6: - reg |= 0xB; - break; - case ADV7183_COMPOSITE7: - reg |= 0xC; - break; - case ADV7183_COMPOSITE8: - reg |= 0xD; - break; - case ADV7183_COMPOSITE9: - reg |= 0xE; - break; - case ADV7183_COMPOSITE10: - reg |= 0xF; - break; - case ADV7183_SVIDEO0: - reg |= 0x6; - break; - case ADV7183_SVIDEO1: - reg |= 0x7; - break; - case ADV7183_SVIDEO2: - reg |= 0x8; - break; - case ADV7183_COMPONENT0: - reg |= 0x9; - break; - case ADV7183_COMPONENT1: - reg |= 0xA; - break; - default: - break; - } - adv7183_write(sd, ADV7183_IN_CTRL, reg); - } - - if (output != decoder->output) { - decoder->output = output; - reg = adv7183_read(sd, ADV7183_OUT_CTRL) & 0xC0; - switch (output) { - case ADV7183_16BIT_OUT: - reg |= 0x9; - break; - default: - reg |= 0xC; - break; - } - adv7183_write(sd, ADV7183_OUT_CTRL, reg); - } - - return 0; -} - -static int adv7183_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - int val = ctrl->val; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - if (val < 0) - val = 127 - val; - adv7183_write(sd, ADV7183_BRIGHTNESS, val); - break; - case V4L2_CID_CONTRAST: - adv7183_write(sd, ADV7183_CONTRAST, val); - break; - case V4L2_CID_SATURATION: - adv7183_write(sd, ADV7183_SD_SATURATION_CB, val >> 8); - adv7183_write(sd, ADV7183_SD_SATURATION_CR, (val & 0xFF)); - break; - case V4L2_CID_HUE: - adv7183_write(sd, ADV7183_SD_OFFSET_CB, val >> 8); - adv7183_write(sd, ADV7183_SD_OFFSET_CR, (val & 0xFF)); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int adv7183_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) -{ - struct adv7183 *decoder = to_adv7183(sd); - int reg; - - /* enable autodetection block */ - reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF; - adv7183_write(sd, ADV7183_IN_CTRL, reg); - - /* wait autodetection switch */ - mdelay(10); - - /* get autodetection result */ - reg = adv7183_read(sd, ADV7183_STATUS_1); - switch ((reg >> 0x4) & 0x7) { - case 0: - *std = V4L2_STD_NTSC; - break; - case 1: - *std = V4L2_STD_NTSC_443; - break; - case 2: - *std = V4L2_STD_PAL_M; - break; - case 3: - *std = V4L2_STD_PAL_60; - break; - case 4: - *std = V4L2_STD_PAL; - break; - case 5: - *std = V4L2_STD_SECAM; - break; - case 6: - *std = V4L2_STD_PAL_Nc; - break; - case 7: - *std = V4L2_STD_SECAM; - break; - default: - *std = V4L2_STD_UNKNOWN; - break; - } - - /* after std detection, write back user set std */ - adv7183_s_std(sd, decoder->std); - return 0; -} - -static int adv7183_g_input_status(struct v4l2_subdev *sd, u32 *status) -{ - int reg; - - *status = V4L2_IN_ST_NO_SIGNAL; - reg = adv7183_read(sd, ADV7183_STATUS_1); - if (reg < 0) - return reg; - if (reg & 0x1) - *status = 0; - return 0; -} - -static int adv7183_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, - enum v4l2_mbus_pixelcode *code) -{ - if (index > 0) - return -EINVAL; - - *code = V4L2_MBUS_FMT_UYVY8_2X8; - return 0; -} - -static int adv7183_try_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) -{ - struct adv7183 *decoder = to_adv7183(sd); - - fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; - fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; - if (decoder->std & V4L2_STD_525_60) { - fmt->field = V4L2_FIELD_SEQ_TB; - fmt->width = 720; - fmt->height = 480; - } else { - fmt->field = V4L2_FIELD_SEQ_BT; - fmt->width = 720; - fmt->height = 576; - } - return 0; -} - -static int adv7183_s_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) -{ - struct adv7183 *decoder = to_adv7183(sd); - - adv7183_try_mbus_fmt(sd, fmt); - decoder->fmt = *fmt; - return 0; -} - -static int adv7183_g_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) -{ - struct adv7183 *decoder = to_adv7183(sd); - - *fmt = decoder->fmt; - return 0; -} - -static int adv7183_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct adv7183 *decoder = to_adv7183(sd); - - if (enable) - gpio_direction_output(decoder->oe_pin, 0); - else - gpio_direction_output(decoder->oe_pin, 1); - udelay(1); - return 0; -} - -static int adv7183_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *chip) -{ - int rev; - struct i2c_client *client = v4l2_get_subdevdata(sd); - - /* 0x11 for adv7183, 0x13 for adv7183b */ - rev = adv7183_read(sd, ADV7183_IDENT); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7183, rev); -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int adv7183_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - reg->val = adv7183_read(sd, reg->reg & 0xff); - reg->size = 1; - return 0; -} - -static int adv7183_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - adv7183_write(sd, reg->reg & 0xff, reg->val & 0xff); - return 0; -} -#endif - -static const struct v4l2_ctrl_ops adv7183_ctrl_ops = { - .s_ctrl = adv7183_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops adv7183_core_ops = { - .log_status = adv7183_log_status, - .g_std = adv7183_g_std, - .s_std = adv7183_s_std, - .reset = adv7183_reset, - .g_chip_ident = adv7183_g_chip_ident, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = adv7183_g_register, - .s_register = adv7183_s_register, -#endif -}; - -static const struct v4l2_subdev_video_ops adv7183_video_ops = { - .s_routing = adv7183_s_routing, - .querystd = adv7183_querystd, - .g_input_status = adv7183_g_input_status, - .enum_mbus_fmt = adv7183_enum_mbus_fmt, - .try_mbus_fmt = adv7183_try_mbus_fmt, - .s_mbus_fmt = adv7183_s_mbus_fmt, - .g_mbus_fmt = adv7183_g_mbus_fmt, - .s_stream = adv7183_s_stream, -}; - -static const struct v4l2_subdev_ops adv7183_ops = { - .core = &adv7183_core_ops, - .video = &adv7183_video_ops, -}; - -static int adv7183_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct adv7183 *decoder; - struct v4l2_subdev *sd; - struct v4l2_ctrl_handler *hdl; - int ret; - struct v4l2_mbus_framefmt fmt; - const unsigned *pin_array; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - v4l_info(client, "chip found @ 0x%02x (%s)\n", - client->addr << 1, client->adapter->name); - - pin_array = client->dev.platform_data; - if (pin_array == NULL) - return -EINVAL; - - decoder = kzalloc(sizeof(struct adv7183), GFP_KERNEL); - if (decoder == NULL) - return -ENOMEM; - - decoder->reset_pin = pin_array[0]; - decoder->oe_pin = pin_array[1]; - - if (gpio_request(decoder->reset_pin, "ADV7183 Reset")) { - v4l_err(client, "failed to request GPIO %d\n", decoder->reset_pin); - ret = -EBUSY; - goto err_free_decoder; - } - - if (gpio_request(decoder->oe_pin, "ADV7183 Output Enable")) { - v4l_err(client, "failed to request GPIO %d\n", decoder->oe_pin); - ret = -EBUSY; - goto err_free_reset; - } - - sd = &decoder->sd; - v4l2_i2c_subdev_init(sd, client, &adv7183_ops); - - hdl = &decoder->hdl; - v4l2_ctrl_handler_init(hdl, 4); - v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, - V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); - v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, - V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x80); - v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, - V4L2_CID_SATURATION, 0, 0xFFFF, 1, 0x8080); - v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, - V4L2_CID_HUE, 0, 0xFFFF, 1, 0x8080); - /* hook the control handler into the driver */ - sd->ctrl_handler = hdl; - if (hdl->error) { - ret = hdl->error; - - v4l2_ctrl_handler_free(hdl); - goto err_free_oe; - } - - /* v4l2 doesn't support an autodetect standard, pick PAL as default */ - decoder->std = V4L2_STD_PAL; - decoder->input = ADV7183_COMPOSITE4; - decoder->output = ADV7183_8BIT_OUT; - - gpio_direction_output(decoder->oe_pin, 1); - /* reset chip */ - gpio_direction_output(decoder->reset_pin, 0); - /* reset pulse width at least 5ms */ - mdelay(10); - gpio_direction_output(decoder->reset_pin, 1); - /* wait 5ms before any further i2c writes are performed */ - mdelay(5); - - adv7183_writeregs(sd, adv7183_init_regs, ARRAY_SIZE(adv7183_init_regs)); - adv7183_s_std(sd, decoder->std); - fmt.width = 720; - fmt.height = 576; - adv7183_s_mbus_fmt(sd, &fmt); - - /* initialize the hardware to the default control values */ - ret = v4l2_ctrl_handler_setup(hdl); - if (ret) { - v4l2_ctrl_handler_free(hdl); - goto err_free_oe; - } - - return 0; -err_free_oe: - gpio_free(decoder->oe_pin); -err_free_reset: - gpio_free(decoder->reset_pin); -err_free_decoder: - kfree(decoder); - return ret; -} - -static int adv7183_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct adv7183 *decoder = to_adv7183(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(sd->ctrl_handler); - gpio_free(decoder->oe_pin); - gpio_free(decoder->reset_pin); - kfree(decoder); - return 0; -} - -static const struct i2c_device_id adv7183_id[] = { - {"adv7183", 0}, - {}, -}; - -MODULE_DEVICE_TABLE(i2c, adv7183_id); - -static struct i2c_driver adv7183_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "adv7183", - }, - .probe = adv7183_probe, - .remove = __devexit_p(adv7183_remove), - .id_table = adv7183_id, -}; - -static __init int adv7183_init(void) -{ - return i2c_add_driver(&adv7183_driver); -} - -static __exit void adv7183_exit(void) -{ - i2c_del_driver(&adv7183_driver); -} - -module_init(adv7183_init); -module_exit(adv7183_exit); - -MODULE_DESCRIPTION("Analog Devices ADV7183 video decoder driver"); -MODULE_AUTHOR("Scott Jiang "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/adv7183_regs.h b/drivers/media/video/adv7183_regs.h deleted file mode 100644 index 4a5b7d211d2..00000000000 --- a/drivers/media/video/adv7183_regs.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * adv7183 - Analog Devices ADV7183 video decoder registers - * - * Copyright (c) 2011 Analog Devices Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * 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. - */ - -#ifndef _ADV7183_REGS_H_ -#define _ADV7183_REGS_H_ - -#define ADV7183_IN_CTRL 0x00 /* Input control */ -#define ADV7183_VD_SEL 0x01 /* Video selection */ -#define ADV7183_OUT_CTRL 0x03 /* Output control */ -#define ADV7183_EXT_OUT_CTRL 0x04 /* Extended output control */ -#define ADV7183_AUTO_DET_EN 0x07 /* Autodetect enable */ -#define ADV7183_CONTRAST 0x08 /* Contrast */ -#define ADV7183_BRIGHTNESS 0x0A /* Brightness */ -#define ADV7183_HUE 0x0B /* Hue */ -#define ADV7183_DEF_Y 0x0C /* Default value Y */ -#define ADV7183_DEF_C 0x0D /* Default value C */ -#define ADV7183_ADI_CTRL 0x0E /* ADI control */ -#define ADV7183_POW_MANAGE 0x0F /* Power Management */ -#define ADV7183_STATUS_1 0x10 /* Status 1 */ -#define ADV7183_IDENT 0x11 /* Ident */ -#define ADV7183_STATUS_2 0x12 /* Status 2 */ -#define ADV7183_STATUS_3 0x13 /* Status 3 */ -#define ADV7183_ANAL_CLAMP_CTRL 0x14 /* Analog clamp control */ -#define ADV7183_DIGI_CLAMP_CTRL_1 0x15 /* Digital clamp control 1 */ -#define ADV7183_SHAP_FILT_CTRL 0x17 /* Shaping filter control */ -#define ADV7183_SHAP_FILT_CTRL_2 0x18 /* Shaping filter control 2 */ -#define ADV7183_COMB_FILT_CTRL 0x19 /* Comb filter control */ -#define ADV7183_ADI_CTRL_2 0x1D /* ADI control 2 */ -#define ADV7183_PIX_DELAY_CTRL 0x27 /* Pixel delay control */ -#define ADV7183_MISC_GAIN_CTRL 0x2B /* Misc gain control */ -#define ADV7183_AGC_MODE_CTRL 0x2C /* AGC mode control */ -#define ADV7183_CHRO_GAIN_CTRL_1 0x2D /* Chroma gain control 1 */ -#define ADV7183_CHRO_GAIN_CTRL_2 0x2E /* Chroma gain control 2 */ -#define ADV7183_LUMA_GAIN_CTRL_1 0x2F /* Luma gain control 1 */ -#define ADV7183_LUMA_GAIN_CTRL_2 0x30 /* Luma gain control 2 */ -#define ADV7183_VS_FIELD_CTRL_1 0x31 /* Vsync field control 1 */ -#define ADV7183_VS_FIELD_CTRL_2 0x32 /* Vsync field control 2 */ -#define ADV7183_VS_FIELD_CTRL_3 0x33 /* Vsync field control 3 */ -#define ADV7183_HS_POS_CTRL_1 0x34 /* Hsync positon control 1 */ -#define ADV7183_HS_POS_CTRL_2 0x35 /* Hsync positon control 2 */ -#define ADV7183_HS_POS_CTRL_3 0x36 /* Hsync positon control 3 */ -#define ADV7183_POLARITY 0x37 /* Polarity */ -#define ADV7183_NTSC_COMB_CTRL 0x38 /* NTSC comb control */ -#define ADV7183_PAL_COMB_CTRL 0x39 /* PAL comb control */ -#define ADV7183_ADC_CTRL 0x3A /* ADC control */ -#define ADV7183_MAN_WIN_CTRL 0x3D /* Manual window control */ -#define ADV7183_RESAMPLE_CTRL 0x41 /* Resample control */ -#define ADV7183_GEMSTAR_CTRL_1 0x48 /* Gemstar ctrl 1 */ -#define ADV7183_GEMSTAR_CTRL_2 0x49 /* Gemstar ctrl 2 */ -#define ADV7183_GEMSTAR_CTRL_3 0x4A /* Gemstar ctrl 3 */ -#define ADV7183_GEMSTAR_CTRL_4 0x4B /* Gemstar ctrl 4 */ -#define ADV7183_GEMSTAR_CTRL_5 0x4C /* Gemstar ctrl 5 */ -#define ADV7183_CTI_DNR_CTRL_1 0x4D /* CTI DNR ctrl 1 */ -#define ADV7183_CTI_DNR_CTRL_2 0x4E /* CTI DNR ctrl 2 */ -#define ADV7183_CTI_DNR_CTRL_4 0x50 /* CTI DNR ctrl 4 */ -#define ADV7183_LOCK_CNT 0x51 /* Lock count */ -#define ADV7183_FREE_LINE_LEN 0x8F /* Free-Run line length 1 */ -#define ADV7183_VBI_INFO 0x90 /* VBI info */ -#define ADV7183_WSS_1 0x91 /* WSS 1 */ -#define ADV7183_WSS_2 0x92 /* WSS 2 */ -#define ADV7183_EDTV_1 0x93 /* EDTV 1 */ -#define ADV7183_EDTV_2 0x94 /* EDTV 2 */ -#define ADV7183_EDTV_3 0x95 /* EDTV 3 */ -#define ADV7183_CGMS_1 0x96 /* CGMS 1 */ -#define ADV7183_CGMS_2 0x97 /* CGMS 2 */ -#define ADV7183_CGMS_3 0x98 /* CGMS 3 */ -#define ADV7183_CCAP_1 0x99 /* CCAP 1 */ -#define ADV7183_CCAP_2 0x9A /* CCAP 2 */ -#define ADV7183_LETTERBOX_1 0x9B /* Letterbox 1 */ -#define ADV7183_LETTERBOX_2 0x9C /* Letterbox 2 */ -#define ADV7183_LETTERBOX_3 0x9D /* Letterbox 3 */ -#define ADV7183_CRC_EN 0xB2 /* CRC enable */ -#define ADV7183_ADC_SWITCH_1 0xC3 /* ADC switch 1 */ -#define ADV7183_ADC_SWITCH_2 0xC4 /* ADC swithc 2 */ -#define ADV7183_LETTERBOX_CTRL_1 0xDC /* Letterbox control 1 */ -#define ADV7183_LETTERBOX_CTRL_2 0xDD /* Letterbox control 2 */ -#define ADV7183_SD_OFFSET_CB 0xE1 /* SD offset Cb */ -#define ADV7183_SD_OFFSET_CR 0xE2 /* SD offset Cr */ -#define ADV7183_SD_SATURATION_CB 0xE3 /* SD saturation Cb */ -#define ADV7183_SD_SATURATION_CR 0xE4 /* SD saturation Cr */ -#define ADV7183_NTSC_V_BEGIN 0xE5 /* NTSC V bit begin */ -#define ADV7183_NTSC_V_END 0xE6 /* NTSC V bit end */ -#define ADV7183_NTSC_F_TOGGLE 0xE7 /* NTSC F bit toggle */ -#define ADV7183_PAL_V_BEGIN 0xE8 /* PAL V bit begin */ -#define ADV7183_PAL_V_END 0xE9 /* PAL V bit end */ -#define ADV7183_PAL_F_TOGGLE 0xEA /* PAL F bit toggle */ -#define ADV7183_DRIVE_STR 0xF4 /* Drive strength */ -#define ADV7183_IF_COMP_CTRL 0xF8 /* IF comp control */ -#define ADV7183_VS_MODE_CTRL 0xF9 /* VS mode control */ - -#endif diff --git a/drivers/media/video/adv7343.c b/drivers/media/video/adv7343.c deleted file mode 100644 index 2b5aa676a84..00000000000 --- a/drivers/media/video/adv7343.c +++ /dev/null @@ -1,476 +0,0 @@ -/* - * adv7343 - ADV7343 Video Encoder Driver - * - * The encoder hardware does not support SECAM. - * - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ - * - * 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 .as is. WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "adv7343_regs.h" - -MODULE_DESCRIPTION("ADV7343 video encoder driver"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug level 0-1"); - -struct adv7343_state { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - u8 reg00; - u8 reg01; - u8 reg02; - u8 reg35; - u8 reg80; - u8 reg82; - u32 output; - v4l2_std_id std; -}; - -static inline struct adv7343_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct adv7343_state, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct adv7343_state, hdl)->sd; -} - -static inline int adv7343_write(struct v4l2_subdev *sd, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_write_byte_data(client, reg, value); -} - -static const u8 adv7343_init_reg_val[] = { - ADV7343_SOFT_RESET, ADV7343_SOFT_RESET_DEFAULT, - ADV7343_POWER_MODE_REG, ADV7343_POWER_MODE_REG_DEFAULT, - - ADV7343_HD_MODE_REG1, ADV7343_HD_MODE_REG1_DEFAULT, - ADV7343_HD_MODE_REG2, ADV7343_HD_MODE_REG2_DEFAULT, - ADV7343_HD_MODE_REG3, ADV7343_HD_MODE_REG3_DEFAULT, - ADV7343_HD_MODE_REG4, ADV7343_HD_MODE_REG4_DEFAULT, - ADV7343_HD_MODE_REG5, ADV7343_HD_MODE_REG5_DEFAULT, - ADV7343_HD_MODE_REG6, ADV7343_HD_MODE_REG6_DEFAULT, - ADV7343_HD_MODE_REG7, ADV7343_HD_MODE_REG7_DEFAULT, - - ADV7343_SD_MODE_REG1, ADV7343_SD_MODE_REG1_DEFAULT, - ADV7343_SD_MODE_REG2, ADV7343_SD_MODE_REG2_DEFAULT, - ADV7343_SD_MODE_REG3, ADV7343_SD_MODE_REG3_DEFAULT, - ADV7343_SD_MODE_REG4, ADV7343_SD_MODE_REG4_DEFAULT, - ADV7343_SD_MODE_REG5, ADV7343_SD_MODE_REG5_DEFAULT, - ADV7343_SD_MODE_REG6, ADV7343_SD_MODE_REG6_DEFAULT, - ADV7343_SD_MODE_REG7, ADV7343_SD_MODE_REG7_DEFAULT, - ADV7343_SD_MODE_REG8, ADV7343_SD_MODE_REG8_DEFAULT, - - ADV7343_SD_HUE_REG, ADV7343_SD_HUE_REG_DEFAULT, - ADV7343_SD_CGMS_WSS0, ADV7343_SD_CGMS_WSS0_DEFAULT, - ADV7343_SD_BRIGHTNESS_WSS, ADV7343_SD_BRIGHTNESS_WSS_DEFAULT, -}; - -/* - * 2^32 - * FSC(reg) = FSC (HZ) * -------- - * 27000000 - */ -static const struct adv7343_std_info stdinfo[] = { - { - /* FSC(Hz) = 3,579,545.45 Hz */ - SD_STD_NTSC, 569408542, V4L2_STD_NTSC, - }, { - /* FSC(Hz) = 3,575,611.00 Hz */ - SD_STD_PAL_M, 568782678, V4L2_STD_PAL_M, - }, { - /* FSC(Hz) = 3,582,056.00 */ - SD_STD_PAL_N, 569807903, V4L2_STD_PAL_Nc, - }, { - /* FSC(Hz) = 4,433,618.75 Hz */ - SD_STD_PAL_N, 705268427, V4L2_STD_PAL_N, - }, { - /* FSC(Hz) = 4,433,618.75 Hz */ - SD_STD_PAL_BDGHI, 705268427, V4L2_STD_PAL, - }, { - /* FSC(Hz) = 4,433,618.75 Hz */ - SD_STD_NTSC, 705268427, V4L2_STD_NTSC_443, - }, { - /* FSC(Hz) = 4,433,618.75 Hz */ - SD_STD_PAL_M, 705268427, V4L2_STD_PAL_60, - }, -}; - -static int adv7343_setstd(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct adv7343_state *state = to_state(sd); - struct adv7343_std_info *std_info; - int num_std; - char *fsc_ptr; - u8 reg, val; - int err = 0; - int i = 0; - - std_info = (struct adv7343_std_info *)stdinfo; - num_std = ARRAY_SIZE(stdinfo); - - for (i = 0; i < num_std; i++) { - if (std_info[i].stdid & std) - break; - } - - if (i == num_std) { - v4l2_dbg(1, debug, sd, - "Invalid std or std is not supported: %llx\n", - (unsigned long long)std); - return -EINVAL; - } - - /* Set the standard */ - val = state->reg80 & (~(SD_STD_MASK)); - val |= std_info[i].standard_val3; - err = adv7343_write(sd, ADV7343_SD_MODE_REG1, val); - if (err < 0) - goto setstd_exit; - - state->reg80 = val; - - /* Configure the input mode register */ - val = state->reg01 & (~((u8) INPUT_MODE_MASK)); - val |= SD_INPUT_MODE; - err = adv7343_write(sd, ADV7343_MODE_SELECT_REG, val); - if (err < 0) - goto setstd_exit; - - state->reg01 = val; - - /* Program the sub carrier frequency registers */ - fsc_ptr = (unsigned char *)&std_info[i].fsc_val; - reg = ADV7343_FSC_REG0; - for (i = 0; i < 4; i++, reg++, fsc_ptr++) { - err = adv7343_write(sd, reg, *fsc_ptr); - if (err < 0) - goto setstd_exit; - } - - val = state->reg80; - - /* Filter settings */ - if (std & (V4L2_STD_NTSC | V4L2_STD_NTSC_443)) - val &= 0x03; - else if (std & ~V4L2_STD_SECAM) - val |= 0x04; - - err = adv7343_write(sd, ADV7343_SD_MODE_REG1, val); - if (err < 0) - goto setstd_exit; - - state->reg80 = val; - -setstd_exit: - if (err != 0) - v4l2_err(sd, "Error setting std, write failed\n"); - - return err; -} - -static int adv7343_setoutput(struct v4l2_subdev *sd, u32 output_type) -{ - struct adv7343_state *state = to_state(sd); - unsigned char val; - int err = 0; - - if (output_type > ADV7343_SVIDEO_ID) { - v4l2_dbg(1, debug, sd, - "Invalid output type or output type not supported:%d\n", - output_type); - return -EINVAL; - } - - /* Enable Appropriate DAC */ - val = state->reg00 & 0x03; - - if (output_type == ADV7343_COMPOSITE_ID) - val |= ADV7343_COMPOSITE_POWER_VALUE; - else if (output_type == ADV7343_COMPONENT_ID) - val |= ADV7343_COMPONENT_POWER_VALUE; - else - val |= ADV7343_SVIDEO_POWER_VALUE; - - err = adv7343_write(sd, ADV7343_POWER_MODE_REG, val); - if (err < 0) - goto setoutput_exit; - - state->reg00 = val; - - /* Enable YUV output */ - val = state->reg02 | YUV_OUTPUT_SELECT; - err = adv7343_write(sd, ADV7343_MODE_REG0, val); - if (err < 0) - goto setoutput_exit; - - state->reg02 = val; - - /* configure SD DAC Output 2 and SD DAC Output 1 bit to zero */ - val = state->reg82 & (SD_DAC_1_DI & SD_DAC_2_DI); - err = adv7343_write(sd, ADV7343_SD_MODE_REG2, val); - if (err < 0) - goto setoutput_exit; - - state->reg82 = val; - - /* configure ED/HD Color DAC Swap and ED/HD RGB Input Enable bit to - * zero */ - val = state->reg35 & (HD_RGB_INPUT_DI & HD_DAC_SWAP_DI); - err = adv7343_write(sd, ADV7343_HD_MODE_REG6, val); - if (err < 0) - goto setoutput_exit; - - state->reg35 = val; - -setoutput_exit: - if (err != 0) - v4l2_err(sd, "Error setting output, write failed\n"); - - return err; -} - -static int adv7343_log_status(struct v4l2_subdev *sd) -{ - struct adv7343_state *state = to_state(sd); - - v4l2_info(sd, "Standard: %llx\n", (unsigned long long)state->std); - v4l2_info(sd, "Output: %s\n", (state->output == 0) ? "Composite" : - ((state->output == 1) ? "Component" : "S-Video")); - return 0; -} - -static int adv7343_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - return adv7343_write(sd, ADV7343_SD_BRIGHTNESS_WSS, - ctrl->val); - - case V4L2_CID_HUE: - return adv7343_write(sd, ADV7343_SD_HUE_REG, ctrl->val); - - case V4L2_CID_GAIN: - return adv7343_write(sd, ADV7343_DAC2_OUTPUT_LEVEL, ctrl->val); - } - return -EINVAL; -} - -static int adv7343_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7343, 0); -} - -static const struct v4l2_ctrl_ops adv7343_ctrl_ops = { - .s_ctrl = adv7343_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops adv7343_core_ops = { - .log_status = adv7343_log_status, - .g_chip_ident = adv7343_g_chip_ident, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, -}; - -static int adv7343_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct adv7343_state *state = to_state(sd); - int err = 0; - - if (state->std == std) - return 0; - - err = adv7343_setstd(sd, std); - if (!err) - state->std = std; - - return err; -} - -static int adv7343_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct adv7343_state *state = to_state(sd); - int err = 0; - - if (state->output == output) - return 0; - - err = adv7343_setoutput(sd, output); - if (!err) - state->output = output; - - return err; -} - -static const struct v4l2_subdev_video_ops adv7343_video_ops = { - .s_std_output = adv7343_s_std_output, - .s_routing = adv7343_s_routing, -}; - -static const struct v4l2_subdev_ops adv7343_ops = { - .core = &adv7343_core_ops, - .video = &adv7343_video_ops, -}; - -static int adv7343_initialize(struct v4l2_subdev *sd) -{ - struct adv7343_state *state = to_state(sd); - int err = 0; - int i; - - for (i = 0; i < ARRAY_SIZE(adv7343_init_reg_val); i += 2) { - - err = adv7343_write(sd, adv7343_init_reg_val[i], - adv7343_init_reg_val[i+1]); - if (err) { - v4l2_err(sd, "Error initializing\n"); - return err; - } - } - - /* Configure for default video standard */ - err = adv7343_setoutput(sd, state->output); - if (err < 0) { - v4l2_err(sd, "Error setting output during init\n"); - return -EINVAL; - } - - err = adv7343_setstd(sd, state->std); - if (err < 0) { - v4l2_err(sd, "Error setting std during init\n"); - return -EINVAL; - } - - return err; -} - -static int adv7343_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct adv7343_state *state; - int err; - - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - state = kzalloc(sizeof(struct adv7343_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - - state->reg00 = 0x80; - state->reg01 = 0x00; - state->reg02 = 0x20; - state->reg35 = 0x00; - state->reg80 = ADV7343_SD_MODE_REG1_DEFAULT; - state->reg82 = ADV7343_SD_MODE_REG2_DEFAULT; - - state->output = ADV7343_COMPOSITE_ID; - state->std = V4L2_STD_NTSC; - - v4l2_i2c_subdev_init(&state->sd, client, &adv7343_ops); - - v4l2_ctrl_handler_init(&state->hdl, 2); - v4l2_ctrl_new_std(&state->hdl, &adv7343_ctrl_ops, - V4L2_CID_BRIGHTNESS, ADV7343_BRIGHTNESS_MIN, - ADV7343_BRIGHTNESS_MAX, 1, - ADV7343_BRIGHTNESS_DEF); - v4l2_ctrl_new_std(&state->hdl, &adv7343_ctrl_ops, - V4L2_CID_HUE, ADV7343_HUE_MIN, - ADV7343_HUE_MAX, 1, - ADV7343_HUE_DEF); - v4l2_ctrl_new_std(&state->hdl, &adv7343_ctrl_ops, - V4L2_CID_GAIN, ADV7343_GAIN_MIN, - ADV7343_GAIN_MAX, 1, - ADV7343_GAIN_DEF); - state->sd.ctrl_handler = &state->hdl; - if (state->hdl.error) { - int err = state->hdl.error; - - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return err; - } - v4l2_ctrl_handler_setup(&state->hdl); - - err = adv7343_initialize(&state->sd); - if (err) { - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - } - return err; -} - -static int adv7343_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct adv7343_state *state = to_state(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - - return 0; -} - -static const struct i2c_device_id adv7343_id[] = { - {"adv7343", 0}, - {}, -}; - -MODULE_DEVICE_TABLE(i2c, adv7343_id); - -static struct i2c_driver adv7343_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "adv7343", - }, - .probe = adv7343_probe, - .remove = adv7343_remove, - .id_table = adv7343_id, -}; - -module_i2c_driver(adv7343_driver); diff --git a/drivers/media/video/adv7343_regs.h b/drivers/media/video/adv7343_regs.h deleted file mode 100644 index 44660676434..00000000000 --- a/drivers/media/video/adv7343_regs.h +++ /dev/null @@ -1,181 +0,0 @@ -/* - * ADV7343 encoder related structure and register definitions - * - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ - * - * 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 .as is. WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef ADV7343_REG_H -#define ADV7343_REGS_H - -struct adv7343_std_info { - u32 standard_val3; - u32 fsc_val; - v4l2_std_id stdid; -}; - -/* Register offset macros */ -#define ADV7343_POWER_MODE_REG (0x00) -#define ADV7343_MODE_SELECT_REG (0x01) -#define ADV7343_MODE_REG0 (0x02) - -#define ADV7343_DAC2_OUTPUT_LEVEL (0x0b) - -#define ADV7343_SOFT_RESET (0x17) - -#define ADV7343_HD_MODE_REG1 (0x30) -#define ADV7343_HD_MODE_REG2 (0x31) -#define ADV7343_HD_MODE_REG3 (0x32) -#define ADV7343_HD_MODE_REG4 (0x33) -#define ADV7343_HD_MODE_REG5 (0x34) -#define ADV7343_HD_MODE_REG6 (0x35) - -#define ADV7343_HD_MODE_REG7 (0x39) - -#define ADV7343_SD_MODE_REG1 (0x80) -#define ADV7343_SD_MODE_REG2 (0x82) -#define ADV7343_SD_MODE_REG3 (0x83) -#define ADV7343_SD_MODE_REG4 (0x84) -#define ADV7343_SD_MODE_REG5 (0x86) -#define ADV7343_SD_MODE_REG6 (0x87) -#define ADV7343_SD_MODE_REG7 (0x88) -#define ADV7343_SD_MODE_REG8 (0x89) - -#define ADV7343_FSC_REG0 (0x8C) -#define ADV7343_FSC_REG1 (0x8D) -#define ADV7343_FSC_REG2 (0x8E) -#define ADV7343_FSC_REG3 (0x8F) - -#define ADV7343_SD_CGMS_WSS0 (0x99) - -#define ADV7343_SD_HUE_REG (0xA0) -#define ADV7343_SD_BRIGHTNESS_WSS (0xA1) - -/* Default values for the registers */ -#define ADV7343_POWER_MODE_REG_DEFAULT (0x10) -#define ADV7343_HD_MODE_REG1_DEFAULT (0x3C) /* Changed Default - 720p EAVSAV code*/ -#define ADV7343_HD_MODE_REG2_DEFAULT (0x01) /* Changed Pixel data - valid */ -#define ADV7343_HD_MODE_REG3_DEFAULT (0x00) /* Color delay 0 clks */ -#define ADV7343_HD_MODE_REG4_DEFAULT (0xE8) /* Changed */ -#define ADV7343_HD_MODE_REG5_DEFAULT (0x08) -#define ADV7343_HD_MODE_REG6_DEFAULT (0x00) -#define ADV7343_HD_MODE_REG7_DEFAULT (0x00) -#define ADV7343_SD_MODE_REG8_DEFAULT (0x00) -#define ADV7343_SOFT_RESET_DEFAULT (0x02) -#define ADV7343_COMPOSITE_POWER_VALUE (0x80) -#define ADV7343_COMPONENT_POWER_VALUE (0x1C) -#define ADV7343_SVIDEO_POWER_VALUE (0x60) -#define ADV7343_SD_HUE_REG_DEFAULT (127) -#define ADV7343_SD_BRIGHTNESS_WSS_DEFAULT (0x03) - -#define ADV7343_SD_CGMS_WSS0_DEFAULT (0x10) - -#define ADV7343_SD_MODE_REG1_DEFAULT (0x00) -#define ADV7343_SD_MODE_REG2_DEFAULT (0xC9) -#define ADV7343_SD_MODE_REG3_DEFAULT (0x10) -#define ADV7343_SD_MODE_REG4_DEFAULT (0x01) -#define ADV7343_SD_MODE_REG5_DEFAULT (0x02) -#define ADV7343_SD_MODE_REG6_DEFAULT (0x0C) -#define ADV7343_SD_MODE_REG7_DEFAULT (0x04) -#define ADV7343_SD_MODE_REG8_DEFAULT (0x00) - -/* Bit masks for Mode Select Register */ -#define INPUT_MODE_MASK (0x70) -#define SD_INPUT_MODE (0x00) -#define HD_720P_INPUT_MODE (0x10) -#define HD_1080I_INPUT_MODE (0x10) - -/* Bit masks for Mode Register 0 */ -#define TEST_PATTERN_BLACK_BAR_EN (0x04) -#define YUV_OUTPUT_SELECT (0x20) -#define RGB_OUTPUT_SELECT (0xDF) - -/* Bit masks for DAC output levels */ -#define DAC_OUTPUT_LEVEL_MASK (0xFF) - -/* Bit masks for soft reset register */ -#define SOFT_RESET (0x02) - -/* Bit masks for HD Mode Register 1 */ -#define OUTPUT_STD_MASK (0x03) -#define OUTPUT_STD_SHIFT (0) -#define OUTPUT_STD_EIA0_2 (0x00) -#define OUTPUT_STD_EIA0_1 (0x01) -#define OUTPUT_STD_FULL (0x02) -#define EMBEDDED_SYNC (0x04) -#define EXTERNAL_SYNC (0xFB) -#define STD_MODE_SHIFT (3) -#define STD_MODE_MASK (0x1F) -#define STD_MODE_720P (0x05) -#define STD_MODE_720P_25 (0x08) -#define STD_MODE_720P_30 (0x07) -#define STD_MODE_720P_50 (0x06) -#define STD_MODE_1080I (0x0D) -#define STD_MODE_1080I_25fps (0x0E) -#define STD_MODE_1080P_24 (0x12) -#define STD_MODE_1080P_25 (0x10) -#define STD_MODE_1080P_30 (0x0F) -#define STD_MODE_525P (0x00) -#define STD_MODE_625P (0x03) - -/* Bit masks for SD Mode Register 1 */ -#define SD_STD_MASK (0x03) -#define SD_STD_NTSC (0x00) -#define SD_STD_PAL_BDGHI (0x01) -#define SD_STD_PAL_M (0x02) -#define SD_STD_PAL_N (0x03) -#define SD_LUMA_FLTR_MASK (0x7) -#define SD_LUMA_FLTR_SHIFT (0x2) -#define SD_CHROMA_FLTR_MASK (0x7) -#define SD_CHROMA_FLTR_SHIFT (0x5) - -/* Bit masks for SD Mode Register 2 */ -#define SD_PBPR_SSAF_EN (0x01) -#define SD_PBPR_SSAF_DI (0xFE) -#define SD_DAC_1_DI (0xFD) -#define SD_DAC_2_DI (0xFB) -#define SD_PEDESTAL_EN (0x08) -#define SD_PEDESTAL_DI (0xF7) -#define SD_SQUARE_PIXEL_EN (0x10) -#define SD_SQUARE_PIXEL_DI (0xEF) -#define SD_PIXEL_DATA_VALID (0x40) -#define SD_ACTIVE_EDGE_EN (0x80) -#define SD_ACTIVE_EDGE_DI (0x7F) - -/* Bit masks for HD Mode Register 6 */ -#define HD_RGB_INPUT_EN (0x02) -#define HD_RGB_INPUT_DI (0xFD) -#define HD_PBPR_SYNC_EN (0x04) -#define HD_PBPR_SYNC_DI (0xFB) -#define HD_DAC_SWAP_EN (0x08) -#define HD_DAC_SWAP_DI (0xF7) -#define HD_GAMMA_CURVE_A (0xEF) -#define HD_GAMMA_CURVE_B (0x10) -#define HD_GAMMA_EN (0x20) -#define HD_GAMMA_DI (0xDF) -#define HD_ADPT_FLTR_MODEB (0x40) -#define HD_ADPT_FLTR_MODEA (0xBF) -#define HD_ADPT_FLTR_EN (0x80) -#define HD_ADPT_FLTR_DI (0x7F) - -#define ADV7343_BRIGHTNESS_MAX (127) -#define ADV7343_BRIGHTNESS_MIN (0) -#define ADV7343_BRIGHTNESS_DEF (3) -#define ADV7343_HUE_MAX (255) -#define ADV7343_HUE_MIN (0) -#define ADV7343_HUE_DEF (127) -#define ADV7343_GAIN_MAX (64) -#define ADV7343_GAIN_MIN (-64) -#define ADV7343_GAIN_DEF (0) - -#endif diff --git a/drivers/media/video/adv7393.c b/drivers/media/video/adv7393.c deleted file mode 100644 index 3dc6098c726..00000000000 --- a/drivers/media/video/adv7393.c +++ /dev/null @@ -1,487 +0,0 @@ -/* - * adv7393 - ADV7393 Video Encoder Driver - * - * The encoder hardware does not support SECAM. - * - * Copyright (C) 2010-2012 ADVANSEE - http://www.advansee.com/ - * Benoît Thébaudeau - * - * Based on ADV7343 driver, - * - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ - * - * 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 .as is. WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "adv7393_regs.h" - -MODULE_DESCRIPTION("ADV7393 video encoder driver"); -MODULE_LICENSE("GPL"); - -static bool debug; -module_param(debug, bool, 0644); -MODULE_PARM_DESC(debug, "Debug level 0-1"); - -struct adv7393_state { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - u8 reg00; - u8 reg01; - u8 reg02; - u8 reg35; - u8 reg80; - u8 reg82; - u32 output; - v4l2_std_id std; -}; - -static inline struct adv7393_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct adv7393_state, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct adv7393_state, hdl)->sd; -} - -static inline int adv7393_write(struct v4l2_subdev *sd, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_write_byte_data(client, reg, value); -} - -static const u8 adv7393_init_reg_val[] = { - ADV7393_SOFT_RESET, ADV7393_SOFT_RESET_DEFAULT, - ADV7393_POWER_MODE_REG, ADV7393_POWER_MODE_REG_DEFAULT, - - ADV7393_HD_MODE_REG1, ADV7393_HD_MODE_REG1_DEFAULT, - ADV7393_HD_MODE_REG2, ADV7393_HD_MODE_REG2_DEFAULT, - ADV7393_HD_MODE_REG3, ADV7393_HD_MODE_REG3_DEFAULT, - ADV7393_HD_MODE_REG4, ADV7393_HD_MODE_REG4_DEFAULT, - ADV7393_HD_MODE_REG5, ADV7393_HD_MODE_REG5_DEFAULT, - ADV7393_HD_MODE_REG6, ADV7393_HD_MODE_REG6_DEFAULT, - ADV7393_HD_MODE_REG7, ADV7393_HD_MODE_REG7_DEFAULT, - - ADV7393_SD_MODE_REG1, ADV7393_SD_MODE_REG1_DEFAULT, - ADV7393_SD_MODE_REG2, ADV7393_SD_MODE_REG2_DEFAULT, - ADV7393_SD_MODE_REG3, ADV7393_SD_MODE_REG3_DEFAULT, - ADV7393_SD_MODE_REG4, ADV7393_SD_MODE_REG4_DEFAULT, - ADV7393_SD_MODE_REG5, ADV7393_SD_MODE_REG5_DEFAULT, - ADV7393_SD_MODE_REG6, ADV7393_SD_MODE_REG6_DEFAULT, - ADV7393_SD_MODE_REG7, ADV7393_SD_MODE_REG7_DEFAULT, - ADV7393_SD_MODE_REG8, ADV7393_SD_MODE_REG8_DEFAULT, - - ADV7393_SD_TIMING_REG0, ADV7393_SD_TIMING_REG0_DEFAULT, - - ADV7393_SD_HUE_ADJUST, ADV7393_SD_HUE_ADJUST_DEFAULT, - ADV7393_SD_CGMS_WSS0, ADV7393_SD_CGMS_WSS0_DEFAULT, - ADV7393_SD_BRIGHTNESS_WSS, ADV7393_SD_BRIGHTNESS_WSS_DEFAULT, -}; - -/* - * 2^32 - * FSC(reg) = FSC (HZ) * -------- - * 27000000 - */ -static const struct adv7393_std_info stdinfo[] = { - { - /* FSC(Hz) = 4,433,618.75 Hz */ - SD_STD_NTSC, 705268427, V4L2_STD_NTSC_443, - }, { - /* FSC(Hz) = 3,579,545.45 Hz */ - SD_STD_NTSC, 569408542, V4L2_STD_NTSC, - }, { - /* FSC(Hz) = 3,575,611.00 Hz */ - SD_STD_PAL_M, 568782678, V4L2_STD_PAL_M, - }, { - /* FSC(Hz) = 3,582,056.00 Hz */ - SD_STD_PAL_N, 569807903, V4L2_STD_PAL_Nc, - }, { - /* FSC(Hz) = 4,433,618.75 Hz */ - SD_STD_PAL_N, 705268427, V4L2_STD_PAL_N, - }, { - /* FSC(Hz) = 4,433,618.75 Hz */ - SD_STD_PAL_M, 705268427, V4L2_STD_PAL_60, - }, { - /* FSC(Hz) = 4,433,618.75 Hz */ - SD_STD_PAL_BDGHI, 705268427, V4L2_STD_PAL, - }, -}; - -static int adv7393_setstd(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct adv7393_state *state = to_state(sd); - const struct adv7393_std_info *std_info; - int num_std; - u8 reg; - u32 val; - int err = 0; - int i; - - num_std = ARRAY_SIZE(stdinfo); - - for (i = 0; i < num_std; i++) { - if (stdinfo[i].stdid & std) - break; - } - - if (i == num_std) { - v4l2_dbg(1, debug, sd, - "Invalid std or std is not supported: %llx\n", - (unsigned long long)std); - return -EINVAL; - } - - std_info = &stdinfo[i]; - - /* Set the standard */ - val = state->reg80 & ~SD_STD_MASK; - val |= std_info->standard_val3; - err = adv7393_write(sd, ADV7393_SD_MODE_REG1, val); - if (err < 0) - goto setstd_exit; - - state->reg80 = val; - - /* Configure the input mode register */ - val = state->reg01 & ~INPUT_MODE_MASK; - val |= SD_INPUT_MODE; - err = adv7393_write(sd, ADV7393_MODE_SELECT_REG, val); - if (err < 0) - goto setstd_exit; - - state->reg01 = val; - - /* Program the sub carrier frequency registers */ - val = std_info->fsc_val; - for (reg = ADV7393_FSC_REG0; reg <= ADV7393_FSC_REG3; reg++) { - err = adv7393_write(sd, reg, val); - if (err < 0) - goto setstd_exit; - val >>= 8; - } - - val = state->reg82; - - /* Pedestal settings */ - if (std & (V4L2_STD_NTSC | V4L2_STD_NTSC_443)) - val |= SD_PEDESTAL_EN; - else - val &= SD_PEDESTAL_DI; - - err = adv7393_write(sd, ADV7393_SD_MODE_REG2, val); - if (err < 0) - goto setstd_exit; - - state->reg82 = val; - -setstd_exit: - if (err != 0) - v4l2_err(sd, "Error setting std, write failed\n"); - - return err; -} - -static int adv7393_setoutput(struct v4l2_subdev *sd, u32 output_type) -{ - struct adv7393_state *state = to_state(sd); - u8 val; - int err = 0; - - if (output_type > ADV7393_SVIDEO_ID) { - v4l2_dbg(1, debug, sd, - "Invalid output type or output type not supported:%d\n", - output_type); - return -EINVAL; - } - - /* Enable Appropriate DAC */ - val = state->reg00 & 0x03; - - if (output_type == ADV7393_COMPOSITE_ID) - val |= ADV7393_COMPOSITE_POWER_VALUE; - else if (output_type == ADV7393_COMPONENT_ID) - val |= ADV7393_COMPONENT_POWER_VALUE; - else - val |= ADV7393_SVIDEO_POWER_VALUE; - - err = adv7393_write(sd, ADV7393_POWER_MODE_REG, val); - if (err < 0) - goto setoutput_exit; - - state->reg00 = val; - - /* Enable YUV output */ - val = state->reg02 | YUV_OUTPUT_SELECT; - err = adv7393_write(sd, ADV7393_MODE_REG0, val); - if (err < 0) - goto setoutput_exit; - - state->reg02 = val; - - /* configure SD DAC Output 1 bit */ - val = state->reg82; - if (output_type == ADV7393_COMPONENT_ID) - val &= SD_DAC_OUT1_DI; - else - val |= SD_DAC_OUT1_EN; - err = adv7393_write(sd, ADV7393_SD_MODE_REG2, val); - if (err < 0) - goto setoutput_exit; - - state->reg82 = val; - - /* configure ED/HD Color DAC Swap bit to zero */ - val = state->reg35 & HD_DAC_SWAP_DI; - err = adv7393_write(sd, ADV7393_HD_MODE_REG6, val); - if (err < 0) - goto setoutput_exit; - - state->reg35 = val; - -setoutput_exit: - if (err != 0) - v4l2_err(sd, "Error setting output, write failed\n"); - - return err; -} - -static int adv7393_log_status(struct v4l2_subdev *sd) -{ - struct adv7393_state *state = to_state(sd); - - v4l2_info(sd, "Standard: %llx\n", (unsigned long long)state->std); - v4l2_info(sd, "Output: %s\n", (state->output == 0) ? "Composite" : - ((state->output == 1) ? "Component" : "S-Video")); - return 0; -} - -static int adv7393_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - return adv7393_write(sd, ADV7393_SD_BRIGHTNESS_WSS, - ctrl->val & SD_BRIGHTNESS_VALUE_MASK); - - case V4L2_CID_HUE: - return adv7393_write(sd, ADV7393_SD_HUE_ADJUST, - ctrl->val - ADV7393_HUE_MIN); - - case V4L2_CID_GAIN: - return adv7393_write(sd, ADV7393_DAC123_OUTPUT_LEVEL, - ctrl->val); - } - return -EINVAL; -} - -static int adv7393_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7393, 0); -} - -static const struct v4l2_ctrl_ops adv7393_ctrl_ops = { - .s_ctrl = adv7393_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops adv7393_core_ops = { - .log_status = adv7393_log_status, - .g_chip_ident = adv7393_g_chip_ident, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, -}; - -static int adv7393_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct adv7393_state *state = to_state(sd); - int err = 0; - - if (state->std == std) - return 0; - - err = adv7393_setstd(sd, std); - if (!err) - state->std = std; - - return err; -} - -static int adv7393_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct adv7393_state *state = to_state(sd); - int err = 0; - - if (state->output == output) - return 0; - - err = adv7393_setoutput(sd, output); - if (!err) - state->output = output; - - return err; -} - -static const struct v4l2_subdev_video_ops adv7393_video_ops = { - .s_std_output = adv7393_s_std_output, - .s_routing = adv7393_s_routing, -}; - -static const struct v4l2_subdev_ops adv7393_ops = { - .core = &adv7393_core_ops, - .video = &adv7393_video_ops, -}; - -static int adv7393_initialize(struct v4l2_subdev *sd) -{ - struct adv7393_state *state = to_state(sd); - int err = 0; - int i; - - for (i = 0; i < ARRAY_SIZE(adv7393_init_reg_val); i += 2) { - - err = adv7393_write(sd, adv7393_init_reg_val[i], - adv7393_init_reg_val[i+1]); - if (err) { - v4l2_err(sd, "Error initializing\n"); - return err; - } - } - - /* Configure for default video standard */ - err = adv7393_setoutput(sd, state->output); - if (err < 0) { - v4l2_err(sd, "Error setting output during init\n"); - return -EINVAL; - } - - err = adv7393_setstd(sd, state->std); - if (err < 0) { - v4l2_err(sd, "Error setting std during init\n"); - return -EINVAL; - } - - return err; -} - -static int adv7393_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct adv7393_state *state; - int err; - - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - state = kzalloc(sizeof(struct adv7393_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - - state->reg00 = ADV7393_POWER_MODE_REG_DEFAULT; - state->reg01 = 0x00; - state->reg02 = 0x20; - state->reg35 = ADV7393_HD_MODE_REG6_DEFAULT; - state->reg80 = ADV7393_SD_MODE_REG1_DEFAULT; - state->reg82 = ADV7393_SD_MODE_REG2_DEFAULT; - - state->output = ADV7393_COMPOSITE_ID; - state->std = V4L2_STD_NTSC; - - v4l2_i2c_subdev_init(&state->sd, client, &adv7393_ops); - - v4l2_ctrl_handler_init(&state->hdl, 3); - v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops, - V4L2_CID_BRIGHTNESS, ADV7393_BRIGHTNESS_MIN, - ADV7393_BRIGHTNESS_MAX, 1, - ADV7393_BRIGHTNESS_DEF); - v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops, - V4L2_CID_HUE, ADV7393_HUE_MIN, - ADV7393_HUE_MAX, 1, - ADV7393_HUE_DEF); - v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops, - V4L2_CID_GAIN, ADV7393_GAIN_MIN, - ADV7393_GAIN_MAX, 1, - ADV7393_GAIN_DEF); - state->sd.ctrl_handler = &state->hdl; - if (state->hdl.error) { - int err = state->hdl.error; - - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return err; - } - v4l2_ctrl_handler_setup(&state->hdl); - - err = adv7393_initialize(&state->sd); - if (err) { - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - } - return err; -} - -static int adv7393_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct adv7393_state *state = to_state(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - - return 0; -} - -static const struct i2c_device_id adv7393_id[] = { - {"adv7393", 0}, - {}, -}; -MODULE_DEVICE_TABLE(i2c, adv7393_id); - -static struct i2c_driver adv7393_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "adv7393", - }, - .probe = adv7393_probe, - .remove = adv7393_remove, - .id_table = adv7393_id, -}; -module_i2c_driver(adv7393_driver); diff --git a/drivers/media/video/adv7393_regs.h b/drivers/media/video/adv7393_regs.h deleted file mode 100644 index 78968330f0b..00000000000 --- a/drivers/media/video/adv7393_regs.h +++ /dev/null @@ -1,188 +0,0 @@ -/* - * ADV7393 encoder related structure and register definitions - * - * Copyright (C) 2010-2012 ADVANSEE - http://www.advansee.com/ - * Benoît Thébaudeau - * - * Based on ADV7343 driver, - * - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ - * - * 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 .as is. WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef ADV7393_REGS_H -#define ADV7393_REGS_H - -struct adv7393_std_info { - u32 standard_val3; - u32 fsc_val; - v4l2_std_id stdid; -}; - -/* Register offset macros */ -#define ADV7393_POWER_MODE_REG (0x00) -#define ADV7393_MODE_SELECT_REG (0x01) -#define ADV7393_MODE_REG0 (0x02) - -#define ADV7393_DAC123_OUTPUT_LEVEL (0x0B) - -#define ADV7393_SOFT_RESET (0x17) - -#define ADV7393_HD_MODE_REG1 (0x30) -#define ADV7393_HD_MODE_REG2 (0x31) -#define ADV7393_HD_MODE_REG3 (0x32) -#define ADV7393_HD_MODE_REG4 (0x33) -#define ADV7393_HD_MODE_REG5 (0x34) -#define ADV7393_HD_MODE_REG6 (0x35) - -#define ADV7393_HD_MODE_REG7 (0x39) - -#define ADV7393_SD_MODE_REG1 (0x80) -#define ADV7393_SD_MODE_REG2 (0x82) -#define ADV7393_SD_MODE_REG3 (0x83) -#define ADV7393_SD_MODE_REG4 (0x84) -#define ADV7393_SD_MODE_REG5 (0x86) -#define ADV7393_SD_MODE_REG6 (0x87) -#define ADV7393_SD_MODE_REG7 (0x88) -#define ADV7393_SD_MODE_REG8 (0x89) - -#define ADV7393_SD_TIMING_REG0 (0x8A) - -#define ADV7393_FSC_REG0 (0x8C) -#define ADV7393_FSC_REG1 (0x8D) -#define ADV7393_FSC_REG2 (0x8E) -#define ADV7393_FSC_REG3 (0x8F) - -#define ADV7393_SD_CGMS_WSS0 (0x99) - -#define ADV7393_SD_HUE_ADJUST (0xA0) -#define ADV7393_SD_BRIGHTNESS_WSS (0xA1) - -/* Default values for the registers */ -#define ADV7393_POWER_MODE_REG_DEFAULT (0x10) -#define ADV7393_HD_MODE_REG1_DEFAULT (0x3C) /* Changed Default - 720p EAV/SAV code*/ -#define ADV7393_HD_MODE_REG2_DEFAULT (0x01) /* Changed Pixel data - valid */ -#define ADV7393_HD_MODE_REG3_DEFAULT (0x00) /* Color delay 0 clks */ -#define ADV7393_HD_MODE_REG4_DEFAULT (0xEC) /* Changed */ -#define ADV7393_HD_MODE_REG5_DEFAULT (0x08) -#define ADV7393_HD_MODE_REG6_DEFAULT (0x00) -#define ADV7393_HD_MODE_REG7_DEFAULT (0x00) -#define ADV7393_SOFT_RESET_DEFAULT (0x02) -#define ADV7393_COMPOSITE_POWER_VALUE (0x10) -#define ADV7393_COMPONENT_POWER_VALUE (0x1C) -#define ADV7393_SVIDEO_POWER_VALUE (0x0C) -#define ADV7393_SD_HUE_ADJUST_DEFAULT (0x80) -#define ADV7393_SD_BRIGHTNESS_WSS_DEFAULT (0x00) - -#define ADV7393_SD_CGMS_WSS0_DEFAULT (0x10) - -#define ADV7393_SD_MODE_REG1_DEFAULT (0x10) -#define ADV7393_SD_MODE_REG2_DEFAULT (0xC9) -#define ADV7393_SD_MODE_REG3_DEFAULT (0x00) -#define ADV7393_SD_MODE_REG4_DEFAULT (0x00) -#define ADV7393_SD_MODE_REG5_DEFAULT (0x02) -#define ADV7393_SD_MODE_REG6_DEFAULT (0x8C) -#define ADV7393_SD_MODE_REG7_DEFAULT (0x14) -#define ADV7393_SD_MODE_REG8_DEFAULT (0x00) - -#define ADV7393_SD_TIMING_REG0_DEFAULT (0x0C) - -/* Bit masks for Mode Select Register */ -#define INPUT_MODE_MASK (0x70) -#define SD_INPUT_MODE (0x00) -#define HD_720P_INPUT_MODE (0x10) -#define HD_1080I_INPUT_MODE (0x10) - -/* Bit masks for Mode Register 0 */ -#define TEST_PATTERN_BLACK_BAR_EN (0x04) -#define YUV_OUTPUT_SELECT (0x20) -#define RGB_OUTPUT_SELECT (0xDF) - -/* Bit masks for SD brightness/WSS */ -#define SD_BRIGHTNESS_VALUE_MASK (0x7F) -#define SD_BLANK_WSS_DATA_MASK (0x80) - -/* Bit masks for soft reset register */ -#define SOFT_RESET (0x02) - -/* Bit masks for HD Mode Register 1 */ -#define OUTPUT_STD_MASK (0x03) -#define OUTPUT_STD_SHIFT (0) -#define OUTPUT_STD_EIA0_2 (0x00) -#define OUTPUT_STD_EIA0_1 (0x01) -#define OUTPUT_STD_FULL (0x02) -#define EMBEDDED_SYNC (0x04) -#define EXTERNAL_SYNC (0xFB) -#define STD_MODE_MASK (0x1F) -#define STD_MODE_SHIFT (3) -#define STD_MODE_720P (0x05) -#define STD_MODE_720P_25 (0x08) -#define STD_MODE_720P_30 (0x07) -#define STD_MODE_720P_50 (0x06) -#define STD_MODE_1080I (0x0D) -#define STD_MODE_1080I_25 (0x0E) -#define STD_MODE_1080P_24 (0x11) -#define STD_MODE_1080P_25 (0x10) -#define STD_MODE_1080P_30 (0x0F) -#define STD_MODE_525P (0x00) -#define STD_MODE_625P (0x03) - -/* Bit masks for SD Mode Register 1 */ -#define SD_STD_MASK (0x03) -#define SD_STD_NTSC (0x00) -#define SD_STD_PAL_BDGHI (0x01) -#define SD_STD_PAL_M (0x02) -#define SD_STD_PAL_N (0x03) -#define SD_LUMA_FLTR_MASK (0x07) -#define SD_LUMA_FLTR_SHIFT (2) -#define SD_CHROMA_FLTR_MASK (0x07) -#define SD_CHROMA_FLTR_SHIFT (5) - -/* Bit masks for SD Mode Register 2 */ -#define SD_PRPB_SSAF_EN (0x01) -#define SD_PRPB_SSAF_DI (0xFE) -#define SD_DAC_OUT1_EN (0x02) -#define SD_DAC_OUT1_DI (0xFD) -#define SD_PEDESTAL_EN (0x08) -#define SD_PEDESTAL_DI (0xF7) -#define SD_SQUARE_PIXEL_EN (0x10) -#define SD_SQUARE_PIXEL_DI (0xEF) -#define SD_PIXEL_DATA_VALID (0x40) -#define SD_ACTIVE_EDGE_EN (0x80) -#define SD_ACTIVE_EDGE_DI (0x7F) - -/* Bit masks for HD Mode Register 6 */ -#define HD_PRPB_SYNC_EN (0x04) -#define HD_PRPB_SYNC_DI (0xFB) -#define HD_DAC_SWAP_EN (0x08) -#define HD_DAC_SWAP_DI (0xF7) -#define HD_GAMMA_CURVE_A (0xEF) -#define HD_GAMMA_CURVE_B (0x10) -#define HD_GAMMA_EN (0x20) -#define HD_GAMMA_DI (0xDF) -#define HD_ADPT_FLTR_MODEA (0xBF) -#define HD_ADPT_FLTR_MODEB (0x40) -#define HD_ADPT_FLTR_EN (0x80) -#define HD_ADPT_FLTR_DI (0x7F) - -#define ADV7393_BRIGHTNESS_MAX (63) -#define ADV7393_BRIGHTNESS_MIN (-64) -#define ADV7393_BRIGHTNESS_DEF (0) -#define ADV7393_HUE_MAX (127) -#define ADV7393_HUE_MIN (-128) -#define ADV7393_HUE_DEF (0) -#define ADV7393_GAIN_MAX (64) -#define ADV7393_GAIN_MIN (-64) -#define ADV7393_GAIN_DEF (0) - -#endif diff --git a/drivers/media/video/ak881x.c b/drivers/media/video/ak881x.c deleted file mode 100644 index ba674656b10..00000000000 --- a/drivers/media/video/ak881x.c +++ /dev/null @@ -1,359 +0,0 @@ -/* - * Driver for AK8813 / AK8814 TV-ecoders from Asahi Kasei Microsystems Co., Ltd. (AKM) - * - * Copyright (C) 2010, Guennadi Liakhovetski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define AK881X_INTERFACE_MODE 0 -#define AK881X_VIDEO_PROCESS1 1 -#define AK881X_VIDEO_PROCESS2 2 -#define AK881X_VIDEO_PROCESS3 3 -#define AK881X_DAC_MODE 5 -#define AK881X_STATUS 0x24 -#define AK881X_DEVICE_ID 0x25 -#define AK881X_DEVICE_REVISION 0x26 - -struct ak881x { - struct v4l2_subdev subdev; - struct ak881x_pdata *pdata; - unsigned int lines; - int id; /* DEVICE_ID code V4L2_IDENT_AK881X code from v4l2-chip-ident.h */ - char revision; /* DEVICE_REVISION content */ -}; - -static int reg_read(struct i2c_client *client, const u8 reg) -{ - return i2c_smbus_read_byte_data(client, reg); -} - -static int reg_write(struct i2c_client *client, const u8 reg, - const u8 data) -{ - return i2c_smbus_write_byte_data(client, reg, data); -} - -static int reg_set(struct i2c_client *client, const u8 reg, - const u8 data, u8 mask) -{ - int ret = reg_read(client, reg); - if (ret < 0) - return ret; - return reg_write(client, reg, (ret & ~mask) | (data & mask)); -} - -static struct ak881x *to_ak881x(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), struct ak881x, subdev); -} - -static int ak881x_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *id) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ak881x *ak881x = to_ak881x(client); - - if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) - return -EINVAL; - - if (id->match.addr != client->addr) - return -ENODEV; - - id->ident = ak881x->id; - id->revision = ak881x->revision; - - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int ak881x_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x26) - return -EINVAL; - - if (reg->match.addr != client->addr) - return -ENODEV; - - reg->val = reg_read(client, reg->reg); - - if (reg->val > 0xffff) - return -EIO; - - return 0; -} - -static int ak881x_s_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x26) - return -EINVAL; - - if (reg->match.addr != client->addr) - return -ENODEV; - - if (reg_write(client, reg->reg, reg->val) < 0) - return -EIO; - - return 0; -} -#endif - -static int ak881x_try_g_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ak881x *ak881x = to_ak881x(client); - - v4l_bound_align_image(&mf->width, 0, 720, 2, - &mf->height, 0, ak881x->lines, 1, 0); - mf->field = V4L2_FIELD_INTERLACED; - mf->code = V4L2_MBUS_FMT_YUYV8_2X8; - mf->colorspace = V4L2_COLORSPACE_SMPTE170M; - - return 0; -} - -static int ak881x_s_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - if (mf->field != V4L2_FIELD_INTERLACED || - mf->code != V4L2_MBUS_FMT_YUYV8_2X8) - return -EINVAL; - - return ak881x_try_g_mbus_fmt(sd, mf); -} - -static int ak881x_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index, - enum v4l2_mbus_pixelcode *code) -{ - if (index) - return -EINVAL; - - *code = V4L2_MBUS_FMT_YUYV8_2X8; - return 0; -} - -static int ak881x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ak881x *ak881x = to_ak881x(client); - - a->bounds.left = 0; - a->bounds.top = 0; - a->bounds.width = 720; - a->bounds.height = ak881x->lines; - a->defrect = a->bounds; - a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - a->pixelaspect.numerator = 1; - a->pixelaspect.denominator = 1; - - return 0; -} - -static int ak881x_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ak881x *ak881x = to_ak881x(client); - u8 vp1; - - if (std == V4L2_STD_NTSC_443) { - vp1 = 3; - ak881x->lines = 480; - } else if (std == V4L2_STD_PAL_M) { - vp1 = 5; - ak881x->lines = 480; - } else if (std == V4L2_STD_PAL_60) { - vp1 = 7; - ak881x->lines = 480; - } else if (std && !(std & ~V4L2_STD_PAL)) { - vp1 = 0xf; - ak881x->lines = 576; - } else if (std && !(std & ~V4L2_STD_NTSC)) { - vp1 = 0; - ak881x->lines = 480; - } else { - /* No SECAM or PAL_N/Nc supported */ - return -EINVAL; - } - - reg_set(client, AK881X_VIDEO_PROCESS1, vp1, 0xf); - - return 0; -} - -static int ak881x_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ak881x *ak881x = to_ak881x(client); - - if (enable) { - u8 dac; - /* For colour-bar testing set bit 6 of AK881X_VIDEO_PROCESS1 */ - /* Default: composite output */ - if (ak881x->pdata->flags & AK881X_COMPONENT) - dac = 3; - else - dac = 4; - /* Turn on the DAC(s) */ - reg_write(client, AK881X_DAC_MODE, dac); - dev_dbg(&client->dev, "chip status 0x%x\n", - reg_read(client, AK881X_STATUS)); - } else { - /* ...and clear bit 6 of AK881X_VIDEO_PROCESS1 here */ - reg_write(client, AK881X_DAC_MODE, 0); - dev_dbg(&client->dev, "chip status 0x%x\n", - reg_read(client, AK881X_STATUS)); - } - - return 0; -} - -static struct v4l2_subdev_core_ops ak881x_subdev_core_ops = { - .g_chip_ident = ak881x_g_chip_ident, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = ak881x_g_register, - .s_register = ak881x_s_register, -#endif -}; - -static struct v4l2_subdev_video_ops ak881x_subdev_video_ops = { - .s_mbus_fmt = ak881x_s_mbus_fmt, - .g_mbus_fmt = ak881x_try_g_mbus_fmt, - .try_mbus_fmt = ak881x_try_g_mbus_fmt, - .cropcap = ak881x_cropcap, - .enum_mbus_fmt = ak881x_enum_mbus_fmt, - .s_std_output = ak881x_s_std_output, - .s_stream = ak881x_s_stream, -}; - -static struct v4l2_subdev_ops ak881x_subdev_ops = { - .core = &ak881x_subdev_core_ops, - .video = &ak881x_subdev_video_ops, -}; - -static int ak881x_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct ak881x *ak881x; - u8 ifmode, data; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - dev_warn(&adapter->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); - return -EIO; - } - - ak881x = kzalloc(sizeof(struct ak881x), GFP_KERNEL); - if (!ak881x) - return -ENOMEM; - - v4l2_i2c_subdev_init(&ak881x->subdev, client, &ak881x_subdev_ops); - - data = reg_read(client, AK881X_DEVICE_ID); - - switch (data) { - case 0x13: - ak881x->id = V4L2_IDENT_AK8813; - break; - case 0x14: - ak881x->id = V4L2_IDENT_AK8814; - break; - default: - dev_err(&client->dev, - "No ak881x chip detected, register read %x\n", data); - kfree(ak881x); - return -ENODEV; - } - - ak881x->revision = reg_read(client, AK881X_DEVICE_REVISION); - ak881x->pdata = client->dev.platform_data; - - if (ak881x->pdata) { - if (ak881x->pdata->flags & AK881X_FIELD) - ifmode = 4; - else - ifmode = 0; - - switch (ak881x->pdata->flags & AK881X_IF_MODE_MASK) { - case AK881X_IF_MODE_BT656: - ifmode |= 1; - break; - case AK881X_IF_MODE_MASTER: - ifmode |= 2; - break; - case AK881X_IF_MODE_SLAVE: - default: - break; - } - - dev_dbg(&client->dev, "IF mode %x\n", ifmode); - - /* - * "Line Blanking No." seems to be the same as the number of - * "black" lines on, e.g., SuperH VOU, whose default value of 20 - * "incidentally" matches ak881x' default - */ - reg_write(client, AK881X_INTERFACE_MODE, ifmode | (20 << 3)); - } - - /* Hardware default: NTSC-M */ - ak881x->lines = 480; - - dev_info(&client->dev, "Detected an ak881x chip ID %x, revision %x\n", - data, ak881x->revision); - - return 0; -} - -static int ak881x_remove(struct i2c_client *client) -{ - struct ak881x *ak881x = to_ak881x(client); - - v4l2_device_unregister_subdev(&ak881x->subdev); - kfree(ak881x); - - return 0; -} - -static const struct i2c_device_id ak881x_id[] = { - { "ak8813", 0 }, - { "ak8814", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ak881x_id); - -static struct i2c_driver ak881x_i2c_driver = { - .driver = { - .name = "ak881x", - }, - .probe = ak881x_probe, - .remove = ak881x_remove, - .id_table = ak881x_id, -}; - -module_i2c_driver(ak881x_i2c_driver); - -MODULE_DESCRIPTION("TV-output driver for ak8813/ak8814"); -MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/aptina-pll.c b/drivers/media/video/aptina-pll.c deleted file mode 100644 index 8153a449846..00000000000 --- a/drivers/media/video/aptina-pll.c +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Aptina Sensor PLL Configuration - * - * Copyright (C) 2012 Laurent Pinchart - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#include -#include -#include -#include -#include - -#include "aptina-pll.h" - -int aptina_pll_calculate(struct device *dev, - const struct aptina_pll_limits *limits, - struct aptina_pll *pll) -{ - unsigned int mf_min; - unsigned int mf_max; - unsigned int p1_min; - unsigned int p1_max; - unsigned int p1; - unsigned int div; - - dev_dbg(dev, "PLL: ext clock %u pix clock %u\n", - pll->ext_clock, pll->pix_clock); - - if (pll->ext_clock < limits->ext_clock_min || - pll->ext_clock > limits->ext_clock_max) { - dev_err(dev, "pll: invalid external clock frequency.\n"); - return -EINVAL; - } - - if (pll->pix_clock == 0 || pll->pix_clock > limits->pix_clock_max) { - dev_err(dev, "pll: invalid pixel clock frequency.\n"); - return -EINVAL; - } - - /* Compute the multiplier M and combined N*P1 divisor. */ - div = gcd(pll->pix_clock, pll->ext_clock); - pll->m = pll->pix_clock / div; - div = pll->ext_clock / div; - - /* We now have the smallest M and N*P1 values that will result in the - * desired pixel clock frequency, but they might be out of the valid - * range. Compute the factor by which we should multiply them given the - * following constraints: - * - * - minimum/maximum multiplier - * - minimum/maximum multiplier output clock frequency assuming the - * minimum/maximum N value - * - minimum/maximum combined N*P1 divisor - */ - mf_min = DIV_ROUND_UP(limits->m_min, pll->m); - mf_min = max(mf_min, limits->out_clock_min / - (pll->ext_clock / limits->n_min * pll->m)); - mf_min = max(mf_min, limits->n_min * limits->p1_min / div); - mf_max = limits->m_max / pll->m; - mf_max = min(mf_max, limits->out_clock_max / - (pll->ext_clock / limits->n_max * pll->m)); - mf_max = min(mf_max, DIV_ROUND_UP(limits->n_max * limits->p1_max, div)); - - dev_dbg(dev, "pll: mf min %u max %u\n", mf_min, mf_max); - if (mf_min > mf_max) { - dev_err(dev, "pll: no valid combined N*P1 divisor.\n"); - return -EINVAL; - } - - /* - * We're looking for the highest acceptable P1 value for which a - * multiplier factor MF exists that fulfills the following conditions: - * - * 1. p1 is in the [p1_min, p1_max] range given by the limits and is - * even - * 2. mf is in the [mf_min, mf_max] range computed above - * 3. div * mf is a multiple of p1, in order to compute - * n = div * mf / p1 - * m = pll->m * mf - * 4. the internal clock frequency, given by ext_clock / n, is in the - * [int_clock_min, int_clock_max] range given by the limits - * 5. the output clock frequency, given by ext_clock / n * m, is in the - * [out_clock_min, out_clock_max] range given by the limits - * - * The first naive approach is to iterate over all p1 values acceptable - * according to (1) and all mf values acceptable according to (2), and - * stop at the first combination that fulfills (3), (4) and (5). This - * has a O(n^2) complexity. - * - * Instead of iterating over all mf values in the [mf_min, mf_max] range - * we can compute the mf increment between two acceptable values - * according to (3) with - * - * mf_inc = p1 / gcd(div, p1) (6) - * - * and round the minimum up to the nearest multiple of mf_inc. This will - * restrict the number of mf values to be checked. - * - * Furthermore, conditions (4) and (5) only restrict the range of - * acceptable p1 and mf values by modifying the minimum and maximum - * limits. (5) can be expressed as - * - * ext_clock / (div * mf / p1) * m * mf >= out_clock_min - * ext_clock / (div * mf / p1) * m * mf <= out_clock_max - * - * or - * - * p1 >= out_clock_min * div / (ext_clock * m) (7) - * p1 <= out_clock_max * div / (ext_clock * m) - * - * Similarly, (4) can be expressed as - * - * mf >= ext_clock * p1 / (int_clock_max * div) (8) - * mf <= ext_clock * p1 / (int_clock_min * div) - * - * We can thus iterate over the restricted p1 range defined by the - * combination of (1) and (7), and then compute the restricted mf range - * defined by the combination of (2), (6) and (8). If the resulting mf - * range is not empty, any value in the mf range is acceptable. We thus - * select the mf lwoer bound and the corresponding p1 value. - */ - if (limits->p1_min == 0) { - dev_err(dev, "pll: P1 minimum value must be >0.\n"); - return -EINVAL; - } - - p1_min = max(limits->p1_min, DIV_ROUND_UP(limits->out_clock_min * div, - pll->ext_clock * pll->m)); - p1_max = min(limits->p1_max, limits->out_clock_max * div / - (pll->ext_clock * pll->m)); - - for (p1 = p1_max & ~1; p1 >= p1_min; p1 -= 2) { - unsigned int mf_inc = p1 / gcd(div, p1); - unsigned int mf_high; - unsigned int mf_low; - - mf_low = roundup(max(mf_min, DIV_ROUND_UP(pll->ext_clock * p1, - limits->int_clock_max * div)), mf_inc); - mf_high = min(mf_max, pll->ext_clock * p1 / - (limits->int_clock_min * div)); - - if (mf_low > mf_high) - continue; - - pll->n = div * mf_low / p1; - pll->m *= mf_low; - pll->p1 = p1; - dev_dbg(dev, "PLL: N %u M %u P1 %u\n", pll->n, pll->m, pll->p1); - return 0; - } - - dev_err(dev, "pll: no valid N and P1 divisors found.\n"); - return -EINVAL; -} -EXPORT_SYMBOL_GPL(aptina_pll_calculate); - -MODULE_DESCRIPTION("Aptina PLL Helpers"); -MODULE_AUTHOR("Laurent Pinchart "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/aptina-pll.h b/drivers/media/video/aptina-pll.h deleted file mode 100644 index b370e341e75..00000000000 --- a/drivers/media/video/aptina-pll.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Aptina Sensor PLL Configuration - * - * Copyright (C) 2012 Laurent Pinchart - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#ifndef __APTINA_PLL_H -#define __APTINA_PLL_H - -struct aptina_pll { - unsigned int ext_clock; - unsigned int pix_clock; - - unsigned int n; - unsigned int m; - unsigned int p1; -}; - -struct aptina_pll_limits { - unsigned int ext_clock_min; - unsigned int ext_clock_max; - unsigned int int_clock_min; - unsigned int int_clock_max; - unsigned int out_clock_min; - unsigned int out_clock_max; - unsigned int pix_clock_max; - - unsigned int n_min; - unsigned int n_max; - unsigned int m_min; - unsigned int m_max; - unsigned int p1_min; - unsigned int p1_max; -}; - -struct device; - -int aptina_pll_calculate(struct device *dev, - const struct aptina_pll_limits *limits, - struct aptina_pll *pll); - -#endif /* __APTINA_PLL_H */ diff --git a/drivers/media/video/as3645a.c b/drivers/media/video/as3645a.c deleted file mode 100644 index c4b03572dce..00000000000 --- a/drivers/media/video/as3645a.c +++ /dev/null @@ -1,888 +0,0 @@ -/* - * drivers/media/video/as3645a.c - AS3645A and LM3555 flash controllers driver - * - * Copyright (C) 2008-2011 Nokia Corporation - * Copyright (c) 2011, Intel Corporation. - * - * Contact: Laurent Pinchart - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - * TODO: - * - Check hardware FSTROBE control when sensor driver add support for this - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include - -#define AS_TIMER_MS_TO_CODE(t) (((t) - 100) / 50) -#define AS_TIMER_CODE_TO_MS(c) (50 * (c) + 100) - -/* Register definitions */ - -/* Read-only Design info register: Reset state: xxxx 0001 */ -#define AS_DESIGN_INFO_REG 0x00 -#define AS_DESIGN_INFO_FACTORY(x) (((x) >> 4)) -#define AS_DESIGN_INFO_MODEL(x) ((x) & 0x0f) - -/* Read-only Version control register: Reset state: 0000 0000 - * for first engineering samples - */ -#define AS_VERSION_CONTROL_REG 0x01 -#define AS_VERSION_CONTROL_RFU(x) (((x) >> 4)) -#define AS_VERSION_CONTROL_VERSION(x) ((x) & 0x0f) - -/* Read / Write (Indicator and timer register): Reset state: 0000 1111 */ -#define AS_INDICATOR_AND_TIMER_REG 0x02 -#define AS_INDICATOR_AND_TIMER_TIMEOUT_SHIFT 0 -#define AS_INDICATOR_AND_TIMER_VREF_SHIFT 4 -#define AS_INDICATOR_AND_TIMER_INDICATOR_SHIFT 6 - -/* Read / Write (Current set register): Reset state: 0110 1001 */ -#define AS_CURRENT_SET_REG 0x03 -#define AS_CURRENT_ASSIST_LIGHT_SHIFT 0 -#define AS_CURRENT_LED_DET_ON (1 << 3) -#define AS_CURRENT_FLASH_CURRENT_SHIFT 4 - -/* Read / Write (Control register): Reset state: 1011 0100 */ -#define AS_CONTROL_REG 0x04 -#define AS_CONTROL_MODE_SETTING_SHIFT 0 -#define AS_CONTROL_STROBE_ON (1 << 2) -#define AS_CONTROL_OUT_ON (1 << 3) -#define AS_CONTROL_EXT_TORCH_ON (1 << 4) -#define AS_CONTROL_STROBE_TYPE_EDGE (0 << 5) -#define AS_CONTROL_STROBE_TYPE_LEVEL (1 << 5) -#define AS_CONTROL_COIL_PEAK_SHIFT 6 - -/* Read only (D3 is read / write) (Fault and info): Reset state: 0000 x000 */ -#define AS_FAULT_INFO_REG 0x05 -#define AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT (1 << 1) -#define AS_FAULT_INFO_INDICATOR_LED (1 << 2) -#define AS_FAULT_INFO_LED_AMOUNT (1 << 3) -#define AS_FAULT_INFO_TIMEOUT (1 << 4) -#define AS_FAULT_INFO_OVER_TEMPERATURE (1 << 5) -#define AS_FAULT_INFO_SHORT_CIRCUIT (1 << 6) -#define AS_FAULT_INFO_OVER_VOLTAGE (1 << 7) - -/* Boost register */ -#define AS_BOOST_REG 0x0d -#define AS_BOOST_CURRENT_DISABLE (0 << 0) -#define AS_BOOST_CURRENT_ENABLE (1 << 0) - -/* Password register is used to unlock boost register writing */ -#define AS_PASSWORD_REG 0x0f -#define AS_PASSWORD_UNLOCK_VALUE 0x55 - -enum as_mode { - AS_MODE_EXT_TORCH = 0 << AS_CONTROL_MODE_SETTING_SHIFT, - AS_MODE_INDICATOR = 1 << AS_CONTROL_MODE_SETTING_SHIFT, - AS_MODE_ASSIST = 2 << AS_CONTROL_MODE_SETTING_SHIFT, - AS_MODE_FLASH = 3 << AS_CONTROL_MODE_SETTING_SHIFT, -}; - -/* - * struct as3645a - * - * @subdev: V4L2 subdev - * @pdata: Flash platform data - * @power_lock: Protects power_count - * @power_count: Power reference count - * @led_mode: V4L2 flash LED mode - * @timeout: Flash timeout in microseconds - * @flash_current: Flash current (0=200mA ... 15=500mA). Maximum - * values are 400mA for two LEDs and 500mA for one LED. - * @assist_current: Torch/Assist light current (0=20mA, 1=40mA ... 7=160mA) - * @indicator_current: Indicator LED current (0=0mA, 1=2.5mA ... 4=10mA) - * @strobe_source: Flash strobe source (software or external) - */ -struct as3645a { - struct v4l2_subdev subdev; - const struct as3645a_platform_data *pdata; - - struct mutex power_lock; - int power_count; - - /* Controls */ - struct v4l2_ctrl_handler ctrls; - - enum v4l2_flash_led_mode led_mode; - unsigned int timeout; - u8 flash_current; - u8 assist_current; - u8 indicator_current; - enum v4l2_flash_strobe_source strobe_source; -}; - -#define to_as3645a(sd) container_of(sd, struct as3645a, subdev) - -/* Return negative errno else zero on success */ -static int as3645a_write(struct as3645a *flash, u8 addr, u8 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); - int rval; - - rval = i2c_smbus_write_byte_data(client, addr, val); - - dev_dbg(&client->dev, "Write Addr:%02X Val:%02X %s\n", addr, val, - rval < 0 ? "fail" : "ok"); - - return rval; -} - -/* Return negative errno else a data byte received from the device. */ -static int as3645a_read(struct as3645a *flash, u8 addr) -{ - struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); - int rval; - - rval = i2c_smbus_read_byte_data(client, addr); - - dev_dbg(&client->dev, "Read Addr:%02X Val:%02X %s\n", addr, rval, - rval < 0 ? "fail" : "ok"); - - return rval; -} - -/* ----------------------------------------------------------------------------- - * Hardware configuration and trigger - */ - -/* - * as3645a_set_config - Set flash configuration registers - * @flash: The flash - * - * Configure the hardware with flash, assist and indicator currents, as well as - * flash timeout. - * - * Return 0 on success, or a negative error code if an I2C communication error - * occurred. - */ -static int as3645a_set_config(struct as3645a *flash) -{ - int ret; - u8 val; - - val = (flash->flash_current << AS_CURRENT_FLASH_CURRENT_SHIFT) - | (flash->assist_current << AS_CURRENT_ASSIST_LIGHT_SHIFT) - | AS_CURRENT_LED_DET_ON; - - ret = as3645a_write(flash, AS_CURRENT_SET_REG, val); - if (ret < 0) - return ret; - - val = AS_TIMER_MS_TO_CODE(flash->timeout / 1000) - << AS_INDICATOR_AND_TIMER_TIMEOUT_SHIFT; - - val |= (flash->pdata->vref << AS_INDICATOR_AND_TIMER_VREF_SHIFT) - | ((flash->indicator_current ? flash->indicator_current - 1 : 0) - << AS_INDICATOR_AND_TIMER_INDICATOR_SHIFT); - - return as3645a_write(flash, AS_INDICATOR_AND_TIMER_REG, val); -} - -/* - * as3645a_set_control - Set flash control register - * @flash: The flash - * @mode: Desired output mode - * @on: Desired output state - * - * Configure the hardware with output mode and state. - * - * Return 0 on success, or a negative error code if an I2C communication error - * occurred. - */ -static int -as3645a_set_control(struct as3645a *flash, enum as_mode mode, bool on) -{ - u8 reg; - - /* Configure output parameters and operation mode. */ - reg = (flash->pdata->peak << AS_CONTROL_COIL_PEAK_SHIFT) - | (on ? AS_CONTROL_OUT_ON : 0) - | mode; - - if (flash->led_mode == V4L2_FLASH_LED_MODE_FLASH && - flash->strobe_source == V4L2_FLASH_STROBE_SOURCE_EXTERNAL) { - reg |= AS_CONTROL_STROBE_TYPE_LEVEL - | AS_CONTROL_STROBE_ON; - } - - return as3645a_write(flash, AS_CONTROL_REG, reg); -} - -/* - * as3645a_set_output - Configure output and operation mode - * @flash: Flash controller - * @strobe: Strobe the flash (only valid in flash mode) - * - * Turn the LEDs output on/off and set the operation mode based on the current - * parameters. - * - * The AS3645A can't control the indicator LED independently of the flash/torch - * LED. If the flash controller is in V4L2_FLASH_LED_MODE_NONE mode, set the - * chip to indicator mode. Otherwise set it to assist light (torch) or flash - * mode. - * - * In indicator and assist modes, turn the output on/off based on the indicator - * and torch currents. In software strobe flash mode, turn the output on/off - * based on the strobe parameter. - */ -static int as3645a_set_output(struct as3645a *flash, bool strobe) -{ - enum as_mode mode; - bool on; - - switch (flash->led_mode) { - case V4L2_FLASH_LED_MODE_NONE: - on = flash->indicator_current != 0; - mode = AS_MODE_INDICATOR; - break; - case V4L2_FLASH_LED_MODE_TORCH: - on = true; - mode = AS_MODE_ASSIST; - break; - case V4L2_FLASH_LED_MODE_FLASH: - on = strobe; - mode = AS_MODE_FLASH; - break; - default: - BUG(); - } - - /* Configure output parameters and operation mode. */ - return as3645a_set_control(flash, mode, on); -} - -/* ----------------------------------------------------------------------------- - * V4L2 controls - */ - -static int as3645a_is_active(struct as3645a *flash) -{ - int ret; - - ret = as3645a_read(flash, AS_CONTROL_REG); - return ret < 0 ? ret : !!(ret & AS_CONTROL_OUT_ON); -} - -static int as3645a_read_fault(struct as3645a *flash) -{ - struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); - int rval; - - /* NOTE: reading register clear fault status */ - rval = as3645a_read(flash, AS_FAULT_INFO_REG); - if (rval < 0) - return rval; - - if (rval & AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT) - dev_dbg(&client->dev, "Inductor Peak limit fault\n"); - - if (rval & AS_FAULT_INFO_INDICATOR_LED) - dev_dbg(&client->dev, "Indicator LED fault: " - "Short circuit or open loop\n"); - - dev_dbg(&client->dev, "%u connected LEDs\n", - rval & AS_FAULT_INFO_LED_AMOUNT ? 2 : 1); - - if (rval & AS_FAULT_INFO_TIMEOUT) - dev_dbg(&client->dev, "Timeout fault\n"); - - if (rval & AS_FAULT_INFO_OVER_TEMPERATURE) - dev_dbg(&client->dev, "Over temperature fault\n"); - - if (rval & AS_FAULT_INFO_SHORT_CIRCUIT) - dev_dbg(&client->dev, "Short circuit fault\n"); - - if (rval & AS_FAULT_INFO_OVER_VOLTAGE) - dev_dbg(&client->dev, "Over voltage fault: " - "Indicates missing capacitor or open connection\n"); - - return rval; -} - -static int as3645a_get_ctrl(struct v4l2_ctrl *ctrl) -{ - struct as3645a *flash = - container_of(ctrl->handler, struct as3645a, ctrls); - struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); - int value; - - switch (ctrl->id) { - case V4L2_CID_FLASH_FAULT: - value = as3645a_read_fault(flash); - if (value < 0) - return value; - - ctrl->cur.val = 0; - if (value & AS_FAULT_INFO_SHORT_CIRCUIT) - ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT; - if (value & AS_FAULT_INFO_OVER_TEMPERATURE) - ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE; - if (value & AS_FAULT_INFO_TIMEOUT) - ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT; - if (value & AS_FAULT_INFO_OVER_VOLTAGE) - ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE; - if (value & AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT) - ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_CURRENT; - if (value & AS_FAULT_INFO_INDICATOR_LED) - ctrl->cur.val |= V4L2_FLASH_FAULT_INDICATOR; - break; - - case V4L2_CID_FLASH_STROBE_STATUS: - if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) { - ctrl->cur.val = 0; - break; - } - - value = as3645a_is_active(flash); - if (value < 0) - return value; - - ctrl->cur.val = value; - break; - } - - dev_dbg(&client->dev, "G_CTRL %08x:%d\n", ctrl->id, ctrl->cur.val); - - return 0; -} - -static int as3645a_set_ctrl(struct v4l2_ctrl *ctrl) -{ - struct as3645a *flash = - container_of(ctrl->handler, struct as3645a, ctrls); - struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); - int ret; - - dev_dbg(&client->dev, "S_CTRL %08x:%d\n", ctrl->id, ctrl->val); - - /* If a control that doesn't apply to the current mode is modified, - * we store the value and return immediately. The setting will be - * applied when the LED mode is changed. Otherwise we apply the setting - * immediately. - */ - - switch (ctrl->id) { - case V4L2_CID_FLASH_LED_MODE: - if (flash->indicator_current) - return -EBUSY; - - ret = as3645a_set_config(flash); - if (ret < 0) - return ret; - - flash->led_mode = ctrl->val; - return as3645a_set_output(flash, false); - - case V4L2_CID_FLASH_STROBE_SOURCE: - flash->strobe_source = ctrl->val; - - /* Applies to flash mode only. */ - if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) - break; - - return as3645a_set_output(flash, false); - - case V4L2_CID_FLASH_STROBE: - if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) - return -EBUSY; - - return as3645a_set_output(flash, true); - - case V4L2_CID_FLASH_STROBE_STOP: - if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) - return -EBUSY; - - return as3645a_set_output(flash, false); - - case V4L2_CID_FLASH_TIMEOUT: - flash->timeout = ctrl->val; - - /* Applies to flash mode only. */ - if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) - break; - - return as3645a_set_config(flash); - - case V4L2_CID_FLASH_INTENSITY: - flash->flash_current = (ctrl->val - AS3645A_FLASH_INTENSITY_MIN) - / AS3645A_FLASH_INTENSITY_STEP; - - /* Applies to flash mode only. */ - if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) - break; - - return as3645a_set_config(flash); - - case V4L2_CID_FLASH_TORCH_INTENSITY: - flash->assist_current = - (ctrl->val - AS3645A_TORCH_INTENSITY_MIN) - / AS3645A_TORCH_INTENSITY_STEP; - - /* Applies to torch mode only. */ - if (flash->led_mode != V4L2_FLASH_LED_MODE_TORCH) - break; - - return as3645a_set_config(flash); - - case V4L2_CID_FLASH_INDICATOR_INTENSITY: - if (flash->led_mode != V4L2_FLASH_LED_MODE_NONE) - return -EBUSY; - - flash->indicator_current = - (ctrl->val - AS3645A_INDICATOR_INTENSITY_MIN) - / AS3645A_INDICATOR_INTENSITY_STEP; - - ret = as3645a_set_config(flash); - if (ret < 0) - return ret; - - if ((ctrl->val == 0) == (ctrl->cur.val == 0)) - break; - - return as3645a_set_output(flash, false); - } - - return 0; -} - -static const struct v4l2_ctrl_ops as3645a_ctrl_ops = { - .g_volatile_ctrl = as3645a_get_ctrl, - .s_ctrl = as3645a_set_ctrl, -}; - -/* ----------------------------------------------------------------------------- - * V4L2 subdev core operations - */ - -/* Put device into know state. */ -static int as3645a_setup(struct as3645a *flash) -{ - struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); - int ret; - - /* clear errors */ - ret = as3645a_read(flash, AS_FAULT_INFO_REG); - if (ret < 0) - return ret; - - dev_dbg(&client->dev, "Fault info: %02x\n", ret); - - ret = as3645a_set_config(flash); - if (ret < 0) - return ret; - - ret = as3645a_set_output(flash, false); - if (ret < 0) - return ret; - - /* read status */ - ret = as3645a_read_fault(flash); - if (ret < 0) - return ret; - - dev_dbg(&client->dev, "AS_INDICATOR_AND_TIMER_REG: %02x\n", - as3645a_read(flash, AS_INDICATOR_AND_TIMER_REG)); - dev_dbg(&client->dev, "AS_CURRENT_SET_REG: %02x\n", - as3645a_read(flash, AS_CURRENT_SET_REG)); - dev_dbg(&client->dev, "AS_CONTROL_REG: %02x\n", - as3645a_read(flash, AS_CONTROL_REG)); - - return ret & ~AS_FAULT_INFO_LED_AMOUNT ? -EIO : 0; -} - -static int __as3645a_set_power(struct as3645a *flash, int on) -{ - int ret; - - if (!on) - as3645a_set_control(flash, AS_MODE_EXT_TORCH, false); - - if (flash->pdata->set_power) { - ret = flash->pdata->set_power(&flash->subdev, on); - if (ret < 0) - return ret; - } - - if (!on) - return 0; - - ret = as3645a_setup(flash); - if (ret < 0) { - if (flash->pdata->set_power) - flash->pdata->set_power(&flash->subdev, 0); - } - - return ret; -} - -static int as3645a_set_power(struct v4l2_subdev *sd, int on) -{ - struct as3645a *flash = to_as3645a(sd); - int ret = 0; - - mutex_lock(&flash->power_lock); - - if (flash->power_count == !on) { - ret = __as3645a_set_power(flash, !!on); - if (ret < 0) - goto done; - } - - flash->power_count += on ? 1 : -1; - WARN_ON(flash->power_count < 0); - -done: - mutex_unlock(&flash->power_lock); - return ret; -} - -static int as3645a_registered(struct v4l2_subdev *sd) -{ - struct as3645a *flash = to_as3645a(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - int rval, man, model, rfu, version; - const char *vendor; - - /* Power up the flash driver and read manufacturer ID, model ID, RFU - * and version. - */ - rval = as3645a_set_power(&flash->subdev, 1); - if (rval < 0) - return rval; - - rval = as3645a_read(flash, AS_DESIGN_INFO_REG); - if (rval < 0) - goto power_off; - - man = AS_DESIGN_INFO_FACTORY(rval); - model = AS_DESIGN_INFO_MODEL(rval); - - rval = as3645a_read(flash, AS_VERSION_CONTROL_REG); - if (rval < 0) - goto power_off; - - rfu = AS_VERSION_CONTROL_RFU(rval); - version = AS_VERSION_CONTROL_VERSION(rval); - - /* Verify the chip model and version. */ - if (model != 0x01 || rfu != 0x00) { - dev_err(&client->dev, "AS3645A not detected " - "(model %d rfu %d)\n", model, rfu); - rval = -ENODEV; - goto power_off; - } - - switch (man) { - case 1: - vendor = "AMS, Austria Micro Systems"; - break; - case 2: - vendor = "ADI, Analog Devices Inc."; - break; - case 3: - vendor = "NSC, National Semiconductor"; - break; - case 4: - vendor = "NXP"; - break; - case 5: - vendor = "TI, Texas Instrument"; - break; - default: - vendor = "Unknown"; - } - - dev_info(&client->dev, "Chip vendor: %s (%d) Version: %d\n", vendor, - man, version); - - rval = as3645a_write(flash, AS_PASSWORD_REG, AS_PASSWORD_UNLOCK_VALUE); - if (rval < 0) - goto power_off; - - rval = as3645a_write(flash, AS_BOOST_REG, AS_BOOST_CURRENT_DISABLE); - if (rval < 0) - goto power_off; - - /* Setup default values. This makes sure that the chip is in a known - * state, in case the power rail can't be controlled. - */ - rval = as3645a_setup(flash); - -power_off: - as3645a_set_power(&flash->subdev, 0); - - return rval; -} - -static int as3645a_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - return as3645a_set_power(sd, 1); -} - -static int as3645a_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - return as3645a_set_power(sd, 0); -} - -static const struct v4l2_subdev_core_ops as3645a_core_ops = { - .s_power = as3645a_set_power, -}; - -static const struct v4l2_subdev_ops as3645a_ops = { - .core = &as3645a_core_ops, -}; - -static const struct v4l2_subdev_internal_ops as3645a_internal_ops = { - .registered = as3645a_registered, - .open = as3645a_open, - .close = as3645a_close, -}; - -/* ----------------------------------------------------------------------------- - * I2C driver - */ -#ifdef CONFIG_PM - -static int as3645a_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct as3645a *flash = to_as3645a(subdev); - int rval; - - if (flash->power_count == 0) - return 0; - - rval = __as3645a_set_power(flash, 0); - - dev_dbg(&client->dev, "Suspend %s\n", rval < 0 ? "failed" : "ok"); - - return rval; -} - -static int as3645a_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct as3645a *flash = to_as3645a(subdev); - int rval; - - if (flash->power_count == 0) - return 0; - - rval = __as3645a_set_power(flash, 1); - - dev_dbg(&client->dev, "Resume %s\n", rval < 0 ? "fail" : "ok"); - - return rval; -} - -#else - -#define as3645a_suspend NULL -#define as3645a_resume NULL - -#endif /* CONFIG_PM */ - -/* - * as3645a_init_controls - Create controls - * @flash: The flash - * - * The number of LEDs reported in platform data is used to compute default - * limits. Parameters passed through platform data can override those limits. - */ -static int __devinit as3645a_init_controls(struct as3645a *flash) -{ - const struct as3645a_platform_data *pdata = flash->pdata; - struct v4l2_ctrl *ctrl; - int maximum; - - v4l2_ctrl_handler_init(&flash->ctrls, 10); - - /* V4L2_CID_FLASH_LED_MODE */ - v4l2_ctrl_new_std_menu(&flash->ctrls, &as3645a_ctrl_ops, - V4L2_CID_FLASH_LED_MODE, 2, ~7, - V4L2_FLASH_LED_MODE_NONE); - - /* V4L2_CID_FLASH_STROBE_SOURCE */ - v4l2_ctrl_new_std_menu(&flash->ctrls, &as3645a_ctrl_ops, - V4L2_CID_FLASH_STROBE_SOURCE, - pdata->ext_strobe ? 1 : 0, - pdata->ext_strobe ? ~3 : ~1, - V4L2_FLASH_STROBE_SOURCE_SOFTWARE); - - flash->strobe_source = V4L2_FLASH_STROBE_SOURCE_SOFTWARE; - - /* V4L2_CID_FLASH_STROBE */ - v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, - V4L2_CID_FLASH_STROBE, 0, 0, 0, 0); - - /* V4L2_CID_FLASH_STROBE_STOP */ - v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, - V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0); - - /* V4L2_CID_FLASH_STROBE_STATUS */ - ctrl = v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, - V4L2_CID_FLASH_STROBE_STATUS, 0, 1, 1, 1); - if (ctrl != NULL) - ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; - - /* V4L2_CID_FLASH_TIMEOUT */ - maximum = pdata->timeout_max; - - v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, - V4L2_CID_FLASH_TIMEOUT, AS3645A_FLASH_TIMEOUT_MIN, - maximum, AS3645A_FLASH_TIMEOUT_STEP, maximum); - - flash->timeout = maximum; - - /* V4L2_CID_FLASH_INTENSITY */ - maximum = pdata->flash_max_current; - - v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, - V4L2_CID_FLASH_INTENSITY, AS3645A_FLASH_INTENSITY_MIN, - maximum, AS3645A_FLASH_INTENSITY_STEP, maximum); - - flash->flash_current = (maximum - AS3645A_FLASH_INTENSITY_MIN) - / AS3645A_FLASH_INTENSITY_STEP; - - /* V4L2_CID_FLASH_TORCH_INTENSITY */ - maximum = pdata->torch_max_current; - - v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, - V4L2_CID_FLASH_TORCH_INTENSITY, - AS3645A_TORCH_INTENSITY_MIN, maximum, - AS3645A_TORCH_INTENSITY_STEP, - AS3645A_TORCH_INTENSITY_MIN); - - flash->assist_current = 0; - - /* V4L2_CID_FLASH_INDICATOR_INTENSITY */ - v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, - V4L2_CID_FLASH_INDICATOR_INTENSITY, - AS3645A_INDICATOR_INTENSITY_MIN, - AS3645A_INDICATOR_INTENSITY_MAX, - AS3645A_INDICATOR_INTENSITY_STEP, - AS3645A_INDICATOR_INTENSITY_MIN); - - flash->indicator_current = 0; - - /* V4L2_CID_FLASH_FAULT */ - ctrl = v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, - V4L2_CID_FLASH_FAULT, 0, - V4L2_FLASH_FAULT_OVER_VOLTAGE | - V4L2_FLASH_FAULT_TIMEOUT | - V4L2_FLASH_FAULT_OVER_TEMPERATURE | - V4L2_FLASH_FAULT_SHORT_CIRCUIT, 0, 0); - if (ctrl != NULL) - ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; - - flash->subdev.ctrl_handler = &flash->ctrls; - - return flash->ctrls.error; -} - -static int __devinit as3645a_probe(struct i2c_client *client, - const struct i2c_device_id *devid) -{ - struct as3645a *flash; - int ret; - - if (client->dev.platform_data == NULL) - return -ENODEV; - - flash = kzalloc(sizeof(*flash), GFP_KERNEL); - if (flash == NULL) - return -ENOMEM; - - flash->pdata = client->dev.platform_data; - - v4l2_i2c_subdev_init(&flash->subdev, client, &as3645a_ops); - flash->subdev.internal_ops = &as3645a_internal_ops; - flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - ret = as3645a_init_controls(flash); - if (ret < 0) - goto done; - - ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0); - if (ret < 0) - goto done; - - flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; - - mutex_init(&flash->power_lock); - - flash->led_mode = V4L2_FLASH_LED_MODE_NONE; - -done: - if (ret < 0) { - v4l2_ctrl_handler_free(&flash->ctrls); - kfree(flash); - } - - return ret; -} - -static int __devexit as3645a_remove(struct i2c_client *client) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct as3645a *flash = to_as3645a(subdev); - - v4l2_device_unregister_subdev(subdev); - v4l2_ctrl_handler_free(&flash->ctrls); - media_entity_cleanup(&flash->subdev.entity); - mutex_destroy(&flash->power_lock); - kfree(flash); - - return 0; -} - -static const struct i2c_device_id as3645a_id_table[] = { - { AS3645A_NAME, 0 }, - { }, -}; -MODULE_DEVICE_TABLE(i2c, as3645a_id_table); - -static const struct dev_pm_ops as3645a_pm_ops = { - .suspend = as3645a_suspend, - .resume = as3645a_resume, -}; - -static struct i2c_driver as3645a_i2c_driver = { - .driver = { - .name = AS3645A_NAME, - .pm = &as3645a_pm_ops, - }, - .probe = as3645a_probe, - .remove = __devexit_p(as3645a_remove), - .id_table = as3645a_id_table, -}; - -module_i2c_driver(as3645a_i2c_driver); - -MODULE_AUTHOR("Laurent Pinchart "); -MODULE_DESCRIPTION("LED flash driver for AS3645A, LM3555 and their clones"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/bt819.c b/drivers/media/video/bt819.c deleted file mode 100644 index 377bf05b1ef..00000000000 --- a/drivers/media/video/bt819.c +++ /dev/null @@ -1,517 +0,0 @@ -/* - * bt819 - BT819A VideoStream Decoder (Rockwell Part) - * - * Copyright (C) 1999 Mike Bernson - * Copyright (C) 1998 Dave Perks - * - * Modifications for LML33/DC10plus unified driver - * Copyright (C) 2000 Serguei Miridonov - * - * Changes by Ronald Bultje - * - moved over to linux>=2.4.x i2c protocol (9/9/2002) - * - * This code was modify/ported from the saa7111 driver written - * by Dave Perks. - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("Brooktree-819 video decoder driver"); -MODULE_AUTHOR("Mike Bernson & Dave Perks"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - - -/* ----------------------------------------------------------------------- */ - -struct bt819 { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - unsigned char reg[32]; - - v4l2_std_id norm; - int ident; - int input; - int enable; -}; - -static inline struct bt819 *to_bt819(struct v4l2_subdev *sd) -{ - return container_of(sd, struct bt819, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct bt819, hdl)->sd; -} - -struct timing { - int hactive; - int hdelay; - int vactive; - int vdelay; - int hscale; - int vscale; -}; - -/* for values, see the bt819 datasheet */ -static struct timing timing_data[] = { - {864 - 24, 20, 625 - 2, 1, 0x0504, 0x0000}, - {858 - 24, 20, 525 - 2, 1, 0x00f8, 0x0000}, -}; - -/* ----------------------------------------------------------------------- */ - -static inline int bt819_write(struct bt819 *decoder, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd); - - decoder->reg[reg] = value; - return i2c_smbus_write_byte_data(client, reg, value); -} - -static inline int bt819_setbit(struct bt819 *decoder, u8 reg, u8 bit, u8 value) -{ - return bt819_write(decoder, reg, - (decoder->reg[reg] & ~(1 << bit)) | (value ? (1 << bit) : 0)); -} - -static int bt819_write_block(struct bt819 *decoder, const u8 *data, unsigned int len) -{ - struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd); - int ret = -1; - u8 reg; - - /* the bt819 has an autoincrement function, use it if - * the adapter understands raw I2C */ - if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - /* do raw I2C, not smbus compatible */ - u8 block_data[32]; - int block_len; - - while (len >= 2) { - block_len = 0; - block_data[block_len++] = reg = data[0]; - do { - block_data[block_len++] = - decoder->reg[reg++] = data[1]; - len -= 2; - data += 2; - } while (len >= 2 && data[0] == reg && block_len < 32); - ret = i2c_master_send(client, block_data, block_len); - if (ret < 0) - break; - } - } else { - /* do some slow I2C emulation kind of thing */ - while (len >= 2) { - reg = *data++; - ret = bt819_write(decoder, reg, *data++); - if (ret < 0) - break; - len -= 2; - } - } - - return ret; -} - -static inline int bt819_read(struct bt819 *decoder, u8 reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd); - - return i2c_smbus_read_byte_data(client, reg); -} - -static int bt819_init(struct v4l2_subdev *sd) -{ - static unsigned char init[] = { - /*0x1f, 0x00,*/ /* Reset */ - 0x01, 0x59, /* 0x01 input format */ - 0x02, 0x00, /* 0x02 temporal decimation */ - 0x03, 0x12, /* 0x03 Cropping msb */ - 0x04, 0x16, /* 0x04 Vertical Delay, lsb */ - 0x05, 0xe0, /* 0x05 Vertical Active lsb */ - 0x06, 0x80, /* 0x06 Horizontal Delay lsb */ - 0x07, 0xd0, /* 0x07 Horizontal Active lsb */ - 0x08, 0x00, /* 0x08 Horizontal Scaling msb */ - 0x09, 0xf8, /* 0x09 Horizontal Scaling lsb */ - 0x0a, 0x00, /* 0x0a Brightness control */ - 0x0b, 0x30, /* 0x0b Miscellaneous control */ - 0x0c, 0xd8, /* 0x0c Luma Gain lsb */ - 0x0d, 0xfe, /* 0x0d Chroma Gain (U) lsb */ - 0x0e, 0xb4, /* 0x0e Chroma Gain (V) msb */ - 0x0f, 0x00, /* 0x0f Hue control */ - 0x12, 0x04, /* 0x12 Output Format */ - 0x13, 0x20, /* 0x13 Vertial Scaling msb 0x00 - chroma comb OFF, line drop scaling, interlace scaling - BUG? Why does turning the chroma comb on fuck up color? - Bug in the bt819 stepping on my board? - */ - 0x14, 0x00, /* 0x14 Vertial Scaling lsb */ - 0x16, 0x07, /* 0x16 Video Timing Polarity - ACTIVE=active low - FIELD: high=odd, - vreset=active high, - hreset=active high */ - 0x18, 0x68, /* 0x18 AGC Delay */ - 0x19, 0x5d, /* 0x19 Burst Gate Delay */ - 0x1a, 0x80, /* 0x1a ADC Interface */ - }; - - struct bt819 *decoder = to_bt819(sd); - struct timing *timing = &timing_data[(decoder->norm & V4L2_STD_525_60) ? 1 : 0]; - - init[0x03 * 2 - 1] = - (((timing->vdelay >> 8) & 0x03) << 6) | - (((timing->vactive >> 8) & 0x03) << 4) | - (((timing->hdelay >> 8) & 0x03) << 2) | - ((timing->hactive >> 8) & 0x03); - init[0x04 * 2 - 1] = timing->vdelay & 0xff; - init[0x05 * 2 - 1] = timing->vactive & 0xff; - init[0x06 * 2 - 1] = timing->hdelay & 0xff; - init[0x07 * 2 - 1] = timing->hactive & 0xff; - init[0x08 * 2 - 1] = timing->hscale >> 8; - init[0x09 * 2 - 1] = timing->hscale & 0xff; - /* 0x15 in array is address 0x19 */ - init[0x15 * 2 - 1] = (decoder->norm & V4L2_STD_625_50) ? 115 : 93; /* Chroma burst delay */ - /* reset */ - bt819_write(decoder, 0x1f, 0x00); - mdelay(1); - - /* init */ - return bt819_write_block(decoder, init, sizeof(init)); -} - -/* ----------------------------------------------------------------------- */ - -static int bt819_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd) -{ - struct bt819 *decoder = to_bt819(sd); - int status = bt819_read(decoder, 0x00); - int res = V4L2_IN_ST_NO_SIGNAL; - v4l2_std_id std; - - if ((status & 0x80)) - res = 0; - - if ((status & 0x10)) - std = V4L2_STD_PAL; - else - std = V4L2_STD_NTSC; - if (pstd) - *pstd = std; - if (pstatus) - *pstatus = res; - - v4l2_dbg(1, debug, sd, "get status %x\n", status); - return 0; -} - -static int bt819_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) -{ - return bt819_status(sd, NULL, std); -} - -static int bt819_g_input_status(struct v4l2_subdev *sd, u32 *status) -{ - return bt819_status(sd, status, NULL); -} - -static int bt819_s_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct bt819 *decoder = to_bt819(sd); - struct timing *timing = NULL; - - v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); - - if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL) - v4l2_err(sd, "no notify found!\n"); - - if (std & V4L2_STD_NTSC) { - v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL); - bt819_setbit(decoder, 0x01, 0, 1); - bt819_setbit(decoder, 0x01, 1, 0); - bt819_setbit(decoder, 0x01, 5, 0); - bt819_write(decoder, 0x18, 0x68); - bt819_write(decoder, 0x19, 0x5d); - /* bt819_setbit(decoder, 0x1a, 5, 1); */ - timing = &timing_data[1]; - } else if (std & V4L2_STD_PAL) { - v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL); - bt819_setbit(decoder, 0x01, 0, 1); - bt819_setbit(decoder, 0x01, 1, 1); - bt819_setbit(decoder, 0x01, 5, 1); - bt819_write(decoder, 0x18, 0x7f); - bt819_write(decoder, 0x19, 0x72); - /* bt819_setbit(decoder, 0x1a, 5, 0); */ - timing = &timing_data[0]; - } else { - v4l2_dbg(1, debug, sd, "unsupported norm %llx\n", - (unsigned long long)std); - return -EINVAL; - } - bt819_write(decoder, 0x03, - (((timing->vdelay >> 8) & 0x03) << 6) | - (((timing->vactive >> 8) & 0x03) << 4) | - (((timing->hdelay >> 8) & 0x03) << 2) | - ((timing->hactive >> 8) & 0x03)); - bt819_write(decoder, 0x04, timing->vdelay & 0xff); - bt819_write(decoder, 0x05, timing->vactive & 0xff); - bt819_write(decoder, 0x06, timing->hdelay & 0xff); - bt819_write(decoder, 0x07, timing->hactive & 0xff); - bt819_write(decoder, 0x08, (timing->hscale >> 8) & 0xff); - bt819_write(decoder, 0x09, timing->hscale & 0xff); - decoder->norm = std; - v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, NULL); - return 0; -} - -static int bt819_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct bt819 *decoder = to_bt819(sd); - - v4l2_dbg(1, debug, sd, "set input %x\n", input); - - if (input > 7) - return -EINVAL; - - if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL) - v4l2_err(sd, "no notify found!\n"); - - if (decoder->input != input) { - v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL); - decoder->input = input; - /* select mode */ - if (decoder->input == 0) { - bt819_setbit(decoder, 0x0b, 6, 0); - bt819_setbit(decoder, 0x1a, 1, 1); - } else { - bt819_setbit(decoder, 0x0b, 6, 1); - bt819_setbit(decoder, 0x1a, 1, 0); - } - v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, NULL); - } - return 0; -} - -static int bt819_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct bt819 *decoder = to_bt819(sd); - - v4l2_dbg(1, debug, sd, "enable output %x\n", enable); - - if (decoder->enable != enable) { - decoder->enable = enable; - bt819_setbit(decoder, 0x16, 7, !enable); - } - return 0; -} - -static int bt819_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - struct bt819 *decoder = to_bt819(sd); - int temp; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - bt819_write(decoder, 0x0a, ctrl->val); - break; - - case V4L2_CID_CONTRAST: - bt819_write(decoder, 0x0c, ctrl->val & 0xff); - bt819_setbit(decoder, 0x0b, 2, ((ctrl->val >> 8) & 0x01)); - break; - - case V4L2_CID_SATURATION: - bt819_write(decoder, 0x0d, (ctrl->val >> 7) & 0xff); - bt819_setbit(decoder, 0x0b, 1, ((ctrl->val >> 15) & 0x01)); - - /* Ratio between U gain and V gain must stay the same as - the ratio between the default U and V gain values. */ - temp = (ctrl->val * 180) / 254; - bt819_write(decoder, 0x0e, (temp >> 7) & 0xff); - bt819_setbit(decoder, 0x0b, 0, (temp >> 15) & 0x01); - break; - - case V4L2_CID_HUE: - bt819_write(decoder, 0x0f, ctrl->val); - break; - - default: - return -EINVAL; - } - return 0; -} - -static int bt819_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct bt819 *decoder = to_bt819(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, decoder->ident, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_ctrl_ops bt819_ctrl_ops = { - .s_ctrl = bt819_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops bt819_core_ops = { - .g_chip_ident = bt819_g_chip_ident, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, - .s_std = bt819_s_std, -}; - -static const struct v4l2_subdev_video_ops bt819_video_ops = { - .s_routing = bt819_s_routing, - .s_stream = bt819_s_stream, - .querystd = bt819_querystd, - .g_input_status = bt819_g_input_status, -}; - -static const struct v4l2_subdev_ops bt819_ops = { - .core = &bt819_core_ops, - .video = &bt819_video_ops, -}; - -/* ----------------------------------------------------------------------- */ - -static int bt819_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - int i, ver; - struct bt819 *decoder; - struct v4l2_subdev *sd; - const char *name; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - decoder = kzalloc(sizeof(struct bt819), GFP_KERNEL); - if (decoder == NULL) - return -ENOMEM; - sd = &decoder->sd; - v4l2_i2c_subdev_init(sd, client, &bt819_ops); - - ver = bt819_read(decoder, 0x17); - switch (ver & 0xf0) { - case 0x70: - name = "bt819a"; - decoder->ident = V4L2_IDENT_BT819A; - break; - case 0x60: - name = "bt817a"; - decoder->ident = V4L2_IDENT_BT817A; - break; - case 0x20: - name = "bt815a"; - decoder->ident = V4L2_IDENT_BT815A; - break; - default: - v4l2_dbg(1, debug, sd, - "unknown chip version 0x%02x\n", ver); - return -ENODEV; - } - - v4l_info(client, "%s found @ 0x%x (%s)\n", name, - client->addr << 1, client->adapter->name); - - decoder->norm = V4L2_STD_NTSC; - decoder->input = 0; - decoder->enable = 1; - - i = bt819_init(sd); - if (i < 0) - v4l2_dbg(1, debug, sd, "init status %d\n", i); - - v4l2_ctrl_handler_init(&decoder->hdl, 4); - v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops, - V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); - v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops, - V4L2_CID_CONTRAST, 0, 511, 1, 0xd8); - v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops, - V4L2_CID_SATURATION, 0, 511, 1, 0xfe); - v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops, - V4L2_CID_HUE, -128, 127, 1, 0); - sd->ctrl_handler = &decoder->hdl; - if (decoder->hdl.error) { - int err = decoder->hdl.error; - - v4l2_ctrl_handler_free(&decoder->hdl); - kfree(decoder); - return err; - } - v4l2_ctrl_handler_setup(&decoder->hdl); - return 0; -} - -static int bt819_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct bt819 *decoder = to_bt819(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&decoder->hdl); - kfree(decoder); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id bt819_id[] = { - { "bt819a", 0 }, - { "bt817a", 0 }, - { "bt815a", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, bt819_id); - -static struct i2c_driver bt819_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "bt819", - }, - .probe = bt819_probe, - .remove = bt819_remove, - .id_table = bt819_id, -}; - -module_i2c_driver(bt819_driver); diff --git a/drivers/media/video/bt856.c b/drivers/media/video/bt856.c deleted file mode 100644 index 7e5bd365c23..00000000000 --- a/drivers/media/video/bt856.c +++ /dev/null @@ -1,273 +0,0 @@ -/* - * bt856 - BT856A Digital Video Encoder (Rockwell Part) - * - * Copyright (C) 1999 Mike Bernson - * Copyright (C) 1998 Dave Perks - * - * Modifications for LML33/DC10plus unified driver - * Copyright (C) 2000 Serguei Miridonov - * - * This code was modify/ported from the saa7111 driver written - * by Dave Perks. - * - * Changes by Ronald Bultje - * - moved over to linux>=2.4.x i2c protocol (9/9/2002) - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("Brooktree-856A video encoder driver"); -MODULE_AUTHOR("Mike Bernson & Dave Perks"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - - -/* ----------------------------------------------------------------------- */ - -#define BT856_REG_OFFSET 0xDA -#define BT856_NR_REG 6 - -struct bt856 { - struct v4l2_subdev sd; - unsigned char reg[BT856_NR_REG]; - - v4l2_std_id norm; -}; - -static inline struct bt856 *to_bt856(struct v4l2_subdev *sd) -{ - return container_of(sd, struct bt856, sd); -} - -/* ----------------------------------------------------------------------- */ - -static inline int bt856_write(struct bt856 *encoder, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(&encoder->sd); - - encoder->reg[reg - BT856_REG_OFFSET] = value; - return i2c_smbus_write_byte_data(client, reg, value); -} - -static inline int bt856_setbit(struct bt856 *encoder, u8 reg, u8 bit, u8 value) -{ - return bt856_write(encoder, reg, - (encoder->reg[reg - BT856_REG_OFFSET] & ~(1 << bit)) | - (value ? (1 << bit) : 0)); -} - -static void bt856_dump(struct bt856 *encoder) -{ - int i; - - v4l2_info(&encoder->sd, "register dump:\n"); - for (i = 0; i < BT856_NR_REG; i += 2) - printk(KERN_CONT " %02x", encoder->reg[i]); - printk(KERN_CONT "\n"); -} - -/* ----------------------------------------------------------------------- */ - -static int bt856_init(struct v4l2_subdev *sd, u32 arg) -{ - struct bt856 *encoder = to_bt856(sd); - - /* This is just for testing!!! */ - v4l2_dbg(1, debug, sd, "init\n"); - bt856_write(encoder, 0xdc, 0x18); - bt856_write(encoder, 0xda, 0); - bt856_write(encoder, 0xde, 0); - - bt856_setbit(encoder, 0xdc, 3, 1); - /*bt856_setbit(encoder, 0xdc, 6, 0);*/ - bt856_setbit(encoder, 0xdc, 4, 1); - - if (encoder->norm & V4L2_STD_NTSC) - bt856_setbit(encoder, 0xdc, 2, 0); - else - bt856_setbit(encoder, 0xdc, 2, 1); - - bt856_setbit(encoder, 0xdc, 1, 1); - bt856_setbit(encoder, 0xde, 4, 0); - bt856_setbit(encoder, 0xde, 3, 1); - if (debug != 0) - bt856_dump(encoder); - return 0; -} - -static int bt856_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct bt856 *encoder = to_bt856(sd); - - v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); - - if (std & V4L2_STD_NTSC) { - bt856_setbit(encoder, 0xdc, 2, 0); - } else if (std & V4L2_STD_PAL) { - bt856_setbit(encoder, 0xdc, 2, 1); - bt856_setbit(encoder, 0xda, 0, 0); - /*bt856_setbit(encoder, 0xda, 0, 1);*/ - } else { - return -EINVAL; - } - encoder->norm = std; - if (debug != 0) - bt856_dump(encoder); - return 0; -} - -static int bt856_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct bt856 *encoder = to_bt856(sd); - - v4l2_dbg(1, debug, sd, "set input %d\n", input); - - /* We only have video bus. - * input= 0: input is from bt819 - * input= 1: input is from ZR36060 */ - switch (input) { - case 0: - bt856_setbit(encoder, 0xde, 4, 0); - bt856_setbit(encoder, 0xde, 3, 1); - bt856_setbit(encoder, 0xdc, 3, 1); - bt856_setbit(encoder, 0xdc, 6, 0); - break; - case 1: - bt856_setbit(encoder, 0xde, 4, 0); - bt856_setbit(encoder, 0xde, 3, 1); - bt856_setbit(encoder, 0xdc, 3, 1); - bt856_setbit(encoder, 0xdc, 6, 1); - break; - case 2: /* Color bar */ - bt856_setbit(encoder, 0xdc, 3, 0); - bt856_setbit(encoder, 0xde, 4, 1); - break; - default: - return -EINVAL; - } - - if (debug != 0) - bt856_dump(encoder); - return 0; -} - -static int bt856_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_BT856, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops bt856_core_ops = { - .g_chip_ident = bt856_g_chip_ident, - .init = bt856_init, -}; - -static const struct v4l2_subdev_video_ops bt856_video_ops = { - .s_std_output = bt856_s_std_output, - .s_routing = bt856_s_routing, -}; - -static const struct v4l2_subdev_ops bt856_ops = { - .core = &bt856_core_ops, - .video = &bt856_video_ops, -}; - -/* ----------------------------------------------------------------------- */ - -static int bt856_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct bt856 *encoder; - struct v4l2_subdev *sd; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - encoder = kzalloc(sizeof(struct bt856), GFP_KERNEL); - if (encoder == NULL) - return -ENOMEM; - sd = &encoder->sd; - v4l2_i2c_subdev_init(sd, client, &bt856_ops); - encoder->norm = V4L2_STD_NTSC; - - bt856_write(encoder, 0xdc, 0x18); - bt856_write(encoder, 0xda, 0); - bt856_write(encoder, 0xde, 0); - - bt856_setbit(encoder, 0xdc, 3, 1); - /*bt856_setbit(encoder, 0xdc, 6, 0);*/ - bt856_setbit(encoder, 0xdc, 4, 1); - - if (encoder->norm & V4L2_STD_NTSC) - bt856_setbit(encoder, 0xdc, 2, 0); - else - bt856_setbit(encoder, 0xdc, 2, 1); - - bt856_setbit(encoder, 0xdc, 1, 1); - bt856_setbit(encoder, 0xde, 4, 0); - bt856_setbit(encoder, 0xde, 3, 1); - - if (debug != 0) - bt856_dump(encoder); - return 0; -} - -static int bt856_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - kfree(to_bt856(sd)); - return 0; -} - -static const struct i2c_device_id bt856_id[] = { - { "bt856", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, bt856_id); - -static struct i2c_driver bt856_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "bt856", - }, - .probe = bt856_probe, - .remove = bt856_remove, - .id_table = bt856_id, -}; - -module_i2c_driver(bt856_driver); diff --git a/drivers/media/video/bt866.c b/drivers/media/video/bt866.c deleted file mode 100644 index 905320b67a1..00000000000 --- a/drivers/media/video/bt866.c +++ /dev/null @@ -1,243 +0,0 @@ -/* - bt866 - BT866 Digital Video Encoder (Rockwell Part) - - Copyright (C) 1999 Mike Bernson - Copyright (C) 1998 Dave Perks - - Modifications for LML33/DC10plus unified driver - Copyright (C) 2000 Serguei Miridonov - - This code was modify/ported from the saa7111 driver written - by Dave Perks. - - This code was adapted for the bt866 by Christer Weinigel and ported - to 2.6 by Martin Samuelsson. - - 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 -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("Brooktree-866 video encoder driver"); -MODULE_AUTHOR("Mike Bernson & Dave Perks"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - - -/* ----------------------------------------------------------------------- */ - -struct bt866 { - struct v4l2_subdev sd; - u8 reg[256]; -}; - -static inline struct bt866 *to_bt866(struct v4l2_subdev *sd) -{ - return container_of(sd, struct bt866, sd); -} - -static int bt866_write(struct bt866 *encoder, u8 subaddr, u8 data) -{ - struct i2c_client *client = v4l2_get_subdevdata(&encoder->sd); - u8 buffer[2]; - int err; - - buffer[0] = subaddr; - buffer[1] = data; - - encoder->reg[subaddr] = data; - - v4l_dbg(1, debug, client, "write 0x%02x = 0x%02x\n", subaddr, data); - - for (err = 0; err < 3;) { - if (i2c_master_send(client, buffer, 2) == 2) - break; - err++; - v4l_warn(client, "error #%d writing to 0x%02x\n", - err, subaddr); - schedule_timeout_interruptible(msecs_to_jiffies(100)); - } - if (err == 3) { - v4l_warn(client, "giving up\n"); - return -1; - } - - return 0; -} - -static int bt866_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) -{ - v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); - - /* Only PAL supported by this driver at the moment! */ - if (!(std & V4L2_STD_NTSC)) - return -EINVAL; - return 0; -} - -static int bt866_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - static const __u8 init[] = { - 0xc8, 0xcc, /* CRSCALE */ - 0xca, 0x91, /* CBSCALE */ - 0xcc, 0x24, /* YC16 | OSDNUM */ - 0xda, 0x00, /* */ - 0xdc, 0x24, /* SETMODE | PAL */ - 0xde, 0x02, /* EACTIVE */ - - /* overlay colors */ - 0x70, 0xEB, 0x90, 0x80, 0xB0, 0x80, /* white */ - 0x72, 0xA2, 0x92, 0x8E, 0xB2, 0x2C, /* yellow */ - 0x74, 0x83, 0x94, 0x2C, 0xB4, 0x9C, /* cyan */ - 0x76, 0x70, 0x96, 0x3A, 0xB6, 0x48, /* green */ - 0x78, 0x54, 0x98, 0xC6, 0xB8, 0xB8, /* magenta */ - 0x7A, 0x41, 0x9A, 0xD4, 0xBA, 0x64, /* red */ - 0x7C, 0x23, 0x9C, 0x72, 0xBC, 0xD4, /* blue */ - 0x7E, 0x10, 0x9E, 0x80, 0xBE, 0x80, /* black */ - - 0x60, 0xEB, 0x80, 0x80, 0xc0, 0x80, /* white */ - 0x62, 0xA2, 0x82, 0x8E, 0xc2, 0x2C, /* yellow */ - 0x64, 0x83, 0x84, 0x2C, 0xc4, 0x9C, /* cyan */ - 0x66, 0x70, 0x86, 0x3A, 0xc6, 0x48, /* green */ - 0x68, 0x54, 0x88, 0xC6, 0xc8, 0xB8, /* magenta */ - 0x6A, 0x41, 0x8A, 0xD4, 0xcA, 0x64, /* red */ - 0x6C, 0x23, 0x8C, 0x72, 0xcC, 0xD4, /* blue */ - 0x6E, 0x10, 0x8E, 0x80, 0xcE, 0x80, /* black */ - }; - struct bt866 *encoder = to_bt866(sd); - u8 val; - int i; - - for (i = 0; i < ARRAY_SIZE(init) / 2; i += 2) - bt866_write(encoder, init[i], init[i+1]); - - val = encoder->reg[0xdc]; - - if (input == 0) - val |= 0x40; /* CBSWAP */ - else - val &= ~0x40; /* !CBSWAP */ - - bt866_write(encoder, 0xdc, val); - - val = encoder->reg[0xcc]; - if (input == 2) - val |= 0x01; /* OSDBAR */ - else - val &= ~0x01; /* !OSDBAR */ - bt866_write(encoder, 0xcc, val); - - v4l2_dbg(1, debug, sd, "set input %d\n", input); - - switch (input) { - case 0: - case 1: - case 2: - break; - default: - return -EINVAL; - } - return 0; -} - -#if 0 -/* Code to setup square pixels, might be of some use in the future, - but is currently unused. */ - val = encoder->reg[0xdc]; - if (*iarg) - val |= 1; /* SQUARE */ - else - val &= ~1; /* !SQUARE */ - bt866_write(client, 0xdc, val); -#endif - -static int bt866_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_BT866, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops bt866_core_ops = { - .g_chip_ident = bt866_g_chip_ident, -}; - -static const struct v4l2_subdev_video_ops bt866_video_ops = { - .s_std_output = bt866_s_std_output, - .s_routing = bt866_s_routing, -}; - -static const struct v4l2_subdev_ops bt866_ops = { - .core = &bt866_core_ops, - .video = &bt866_video_ops, -}; - -static int bt866_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct bt866 *encoder; - struct v4l2_subdev *sd; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - encoder = kzalloc(sizeof(*encoder), GFP_KERNEL); - if (encoder == NULL) - return -ENOMEM; - sd = &encoder->sd; - v4l2_i2c_subdev_init(sd, client, &bt866_ops); - return 0; -} - -static int bt866_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - kfree(to_bt866(sd)); - return 0; -} - -static const struct i2c_device_id bt866_id[] = { - { "bt866", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, bt866_id); - -static struct i2c_driver bt866_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "bt866", - }, - .probe = bt866_probe, - .remove = bt866_remove, - .id_table = bt866_id, -}; - -module_i2c_driver(bt866_driver); diff --git a/drivers/media/video/btcx-risc.c b/drivers/media/video/btcx-risc.c deleted file mode 100644 index ac1b2687a20..00000000000 --- a/drivers/media/video/btcx-risc.c +++ /dev/null @@ -1,260 +0,0 @@ -/* - - btcx-risc.c - - bt848/bt878/cx2388x risc code generator. - - (c) 2000-03 Gerd Knorr [SuSE Labs] - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - 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 -#include -#include -#include -#include -#include -#include - -#include "btcx-risc.h" - -MODULE_DESCRIPTION("some code shared by bttv and cx88xx drivers"); -MODULE_AUTHOR("Gerd Knorr"); -MODULE_LICENSE("GPL"); - -static unsigned int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug,"debug messages, default is 0 (no)"); - -/* ---------------------------------------------------------- */ -/* allocate/free risc memory */ - -static int memcnt; - -void btcx_riscmem_free(struct pci_dev *pci, - struct btcx_riscmem *risc) -{ - if (NULL == risc->cpu) - return; - if (debug) { - memcnt--; - printk("btcx: riscmem free [%d] dma=%lx\n", - memcnt, (unsigned long)risc->dma); - } - pci_free_consistent(pci, risc->size, risc->cpu, risc->dma); - memset(risc,0,sizeof(*risc)); -} - -int btcx_riscmem_alloc(struct pci_dev *pci, - struct btcx_riscmem *risc, - unsigned int size) -{ - __le32 *cpu; - dma_addr_t dma = 0; - - if (NULL != risc->cpu && risc->size < size) - btcx_riscmem_free(pci,risc); - if (NULL == risc->cpu) { - cpu = pci_alloc_consistent(pci, size, &dma); - if (NULL == cpu) - return -ENOMEM; - risc->cpu = cpu; - risc->dma = dma; - risc->size = size; - if (debug) { - memcnt++; - printk("btcx: riscmem alloc [%d] dma=%lx cpu=%p size=%d\n", - memcnt, (unsigned long)dma, cpu, size); - } - } - memset(risc->cpu,0,risc->size); - return 0; -} - -/* ---------------------------------------------------------- */ -/* screen overlay helpers */ - -int -btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win, - struct v4l2_clip *clips, unsigned int n) -{ - if (win->left < 0) { - /* left */ - clips[n].c.left = 0; - clips[n].c.top = 0; - clips[n].c.width = -win->left; - clips[n].c.height = win->height; - n++; - } - if (win->left + win->width > swidth) { - /* right */ - clips[n].c.left = swidth - win->left; - clips[n].c.top = 0; - clips[n].c.width = win->width - clips[n].c.left; - clips[n].c.height = win->height; - n++; - } - if (win->top < 0) { - /* top */ - clips[n].c.left = 0; - clips[n].c.top = 0; - clips[n].c.width = win->width; - clips[n].c.height = -win->top; - n++; - } - if (win->top + win->height > sheight) { - /* bottom */ - clips[n].c.left = 0; - clips[n].c.top = sheight - win->top; - clips[n].c.width = win->width; - clips[n].c.height = win->height - clips[n].c.top; - n++; - } - return n; -} - -int -btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, unsigned int n, int mask) -{ - s32 nx,nw,dx; - unsigned int i; - - /* fixup window */ - nx = (win->left + mask) & ~mask; - nw = (win->width) & ~mask; - if (nx + nw > win->left + win->width) - nw -= mask+1; - dx = nx - win->left; - win->left = nx; - win->width = nw; - if (debug) - printk(KERN_DEBUG "btcx: window align %dx%d+%d+%d [dx=%d]\n", - win->width, win->height, win->left, win->top, dx); - - /* fixup clips */ - for (i = 0; i < n; i++) { - nx = (clips[i].c.left-dx) & ~mask; - nw = (clips[i].c.width) & ~mask; - if (nx + nw < clips[i].c.left-dx + clips[i].c.width) - nw += mask+1; - clips[i].c.left = nx; - clips[i].c.width = nw; - if (debug) - printk(KERN_DEBUG "btcx: clip align %dx%d+%d+%d\n", - clips[i].c.width, clips[i].c.height, - clips[i].c.left, clips[i].c.top); - } - return 0; -} - -void -btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips) -{ - struct v4l2_clip swap; - int i,j,n; - - if (nclips < 2) - return; - for (i = nclips-2; i >= 0; i--) { - for (n = 0, j = 0; j <= i; j++) { - if (clips[j].c.left > clips[j+1].c.left) { - swap = clips[j]; - clips[j] = clips[j+1]; - clips[j+1] = swap; - n++; - } - } - if (0 == n) - break; - } -} - -void -btcx_calc_skips(int line, int width, int *maxy, - struct btcx_skiplist *skips, unsigned int *nskips, - const struct v4l2_clip *clips, unsigned int nclips) -{ - unsigned int clip,skip; - int end, maxline; - - skip=0; - maxline = 9999; - for (clip = 0; clip < nclips; clip++) { - - /* sanity checks */ - if (clips[clip].c.left + clips[clip].c.width <= 0) - continue; - if (clips[clip].c.left > (signed)width) - break; - - /* vertical range */ - if (line > clips[clip].c.top+clips[clip].c.height-1) - continue; - if (line < clips[clip].c.top) { - if (maxline > clips[clip].c.top-1) - maxline = clips[clip].c.top-1; - continue; - } - if (maxline > clips[clip].c.top+clips[clip].c.height-1) - maxline = clips[clip].c.top+clips[clip].c.height-1; - - /* horizontal range */ - if (0 == skip || clips[clip].c.left > skips[skip-1].end) { - /* new one */ - skips[skip].start = clips[clip].c.left; - if (skips[skip].start < 0) - skips[skip].start = 0; - skips[skip].end = clips[clip].c.left + clips[clip].c.width; - if (skips[skip].end > width) - skips[skip].end = width; - skip++; - } else { - /* overlaps -- expand last one */ - end = clips[clip].c.left + clips[clip].c.width; - if (skips[skip-1].end < end) - skips[skip-1].end = end; - if (skips[skip-1].end > width) - skips[skip-1].end = width; - } - } - *nskips = skip; - *maxy = maxline; - - if (debug) { - printk(KERN_DEBUG "btcx: skips line %d-%d:",line,maxline); - for (skip = 0; skip < *nskips; skip++) { - printk(" %d-%d",skips[skip].start,skips[skip].end); - } - printk("\n"); - } -} - -/* ---------------------------------------------------------- */ - -EXPORT_SYMBOL(btcx_riscmem_alloc); -EXPORT_SYMBOL(btcx_riscmem_free); - -EXPORT_SYMBOL(btcx_screen_clips); -EXPORT_SYMBOL(btcx_align); -EXPORT_SYMBOL(btcx_sort_clips); -EXPORT_SYMBOL(btcx_calc_skips); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/video/btcx-risc.h b/drivers/media/video/btcx-risc.h deleted file mode 100644 index f8bc6e8e7b5..00000000000 --- a/drivers/media/video/btcx-risc.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - */ -struct btcx_riscmem { - unsigned int size; - __le32 *cpu; - __le32 *jmp; - dma_addr_t dma; -}; - -struct btcx_skiplist { - int start; - int end; -}; - -int btcx_riscmem_alloc(struct pci_dev *pci, - struct btcx_riscmem *risc, - unsigned int size); -void btcx_riscmem_free(struct pci_dev *pci, - struct btcx_riscmem *risc); - -int btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win, - struct v4l2_clip *clips, unsigned int n); -int btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, - unsigned int n, int mask); -void btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips); -void btcx_calc_skips(int line, int width, int *maxy, - struct btcx_skiplist *skips, unsigned int *nskips, - const struct v4l2_clip *clips, unsigned int nclips); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/video/cs5345.c b/drivers/media/video/cs5345.c deleted file mode 100644 index c8581e26fa9..00000000000 --- a/drivers/media/video/cs5345.c +++ /dev/null @@ -1,252 +0,0 @@ -/* - * cs5345 Cirrus Logic 24-bit, 192 kHz Stereo Audio ADC - * Copyright (C) 2007 Hans Verkuil - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("i2c device driver for cs5345 Audio ADC"); -MODULE_AUTHOR("Hans Verkuil"); -MODULE_LICENSE("GPL"); - -static bool debug; - -module_param(debug, bool, 0644); - -MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On"); - -struct cs5345_state { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; -}; - -static inline struct cs5345_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct cs5345_state, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct cs5345_state, hdl)->sd; -} - -/* ----------------------------------------------------------------------- */ - -static inline int cs5345_write(struct v4l2_subdev *sd, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_write_byte_data(client, reg, value); -} - -static inline int cs5345_read(struct v4l2_subdev *sd, u8 reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_read_byte_data(client, reg); -} - -static int cs5345_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - if ((input & 0xf) > 6) { - v4l2_err(sd, "Invalid input %d.\n", input); - return -EINVAL; - } - cs5345_write(sd, 0x09, input & 0xf); - cs5345_write(sd, 0x05, input & 0xf0); - return 0; -} - -static int cs5345_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - cs5345_write(sd, 0x04, ctrl->val ? 0x80 : 0); - return 0; - case V4L2_CID_AUDIO_VOLUME: - cs5345_write(sd, 0x07, ((u8)ctrl->val) & 0x3f); - cs5345_write(sd, 0x08, ((u8)ctrl->val) & 0x3f); - return 0; - } - return -EINVAL; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int cs5345_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - reg->size = 1; - reg->val = cs5345_read(sd, reg->reg & 0x1f); - return 0; -} - -static int cs5345_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - cs5345_write(sd, reg->reg & 0x1f, reg->val & 0xff); - return 0; -} -#endif - -static int cs5345_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_CS5345, 0); -} - -static int cs5345_log_status(struct v4l2_subdev *sd) -{ - u8 v = cs5345_read(sd, 0x09) & 7; - u8 m = cs5345_read(sd, 0x04); - int vol = cs5345_read(sd, 0x08) & 0x3f; - - v4l2_info(sd, "Input: %d%s\n", v, - (m & 0x80) ? " (muted)" : ""); - if (vol >= 32) - vol = vol - 64; - v4l2_info(sd, "Volume: %d dB\n", vol); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_ctrl_ops cs5345_ctrl_ops = { - .s_ctrl = cs5345_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops cs5345_core_ops = { - .log_status = cs5345_log_status, - .g_chip_ident = cs5345_g_chip_ident, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = cs5345_g_register, - .s_register = cs5345_s_register, -#endif -}; - -static const struct v4l2_subdev_audio_ops cs5345_audio_ops = { - .s_routing = cs5345_s_routing, -}; - -static const struct v4l2_subdev_ops cs5345_ops = { - .core = &cs5345_core_ops, - .audio = &cs5345_audio_ops, -}; - -/* ----------------------------------------------------------------------- */ - -static int cs5345_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct cs5345_state *state; - struct v4l2_subdev *sd; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - state = kzalloc(sizeof(struct cs5345_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &cs5345_ops); - - v4l2_ctrl_handler_init(&state->hdl, 2); - v4l2_ctrl_new_std(&state->hdl, &cs5345_ctrl_ops, - V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); - v4l2_ctrl_new_std(&state->hdl, &cs5345_ctrl_ops, - V4L2_CID_AUDIO_VOLUME, -24, 24, 1, 0); - sd->ctrl_handler = &state->hdl; - if (state->hdl.error) { - int err = state->hdl.error; - - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return err; - } - /* set volume/mute */ - v4l2_ctrl_handler_setup(&state->hdl); - - cs5345_write(sd, 0x02, 0x00); - cs5345_write(sd, 0x04, 0x01); - cs5345_write(sd, 0x09, 0x01); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int cs5345_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct cs5345_state *state = to_state(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id cs5345_id[] = { - { "cs5345", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, cs5345_id); - -static struct i2c_driver cs5345_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "cs5345", - }, - .probe = cs5345_probe, - .remove = cs5345_remove, - .id_table = cs5345_id, -}; - -module_i2c_driver(cs5345_driver); diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c deleted file mode 100644 index b293912206e..00000000000 --- a/drivers/media/video/cs53l32a.c +++ /dev/null @@ -1,251 +0,0 @@ -/* - * cs53l32a (Adaptec AVC-2010 and AVC-2410) i2c ivtv driver. - * Copyright (C) 2005 Martin Vaughan - * - * Audio source switching for Adaptec AVC-2410 added by Trev Jackson - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("i2c device driver for cs53l32a Audio ADC"); -MODULE_AUTHOR("Martin Vaughan"); -MODULE_LICENSE("GPL"); - -static bool debug; - -module_param(debug, bool, 0644); - -MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On"); - - -struct cs53l32a_state { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; -}; - -static inline struct cs53l32a_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct cs53l32a_state, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct cs53l32a_state, hdl)->sd; -} - -/* ----------------------------------------------------------------------- */ - -static int cs53l32a_write(struct v4l2_subdev *sd, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_write_byte_data(client, reg, value); -} - -static int cs53l32a_read(struct v4l2_subdev *sd, u8 reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_read_byte_data(client, reg); -} - -static int cs53l32a_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - /* There are 2 physical inputs, but the second input can be - placed in two modes, the first mode bypasses the PGA (gain), - the second goes through the PGA. Hence there are three - possible inputs to choose from. */ - if (input > 2) { - v4l2_err(sd, "Invalid input %d.\n", input); - return -EINVAL; - } - cs53l32a_write(sd, 0x01, 0x01 + (input << 4)); - return 0; -} - -static int cs53l32a_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - cs53l32a_write(sd, 0x03, ctrl->val ? 0xf0 : 0x30); - return 0; - case V4L2_CID_AUDIO_VOLUME: - cs53l32a_write(sd, 0x04, (u8)ctrl->val); - cs53l32a_write(sd, 0x05, (u8)ctrl->val); - return 0; - } - return -EINVAL; -} - -static int cs53l32a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, - chip, V4L2_IDENT_CS53l32A, 0); -} - -static int cs53l32a_log_status(struct v4l2_subdev *sd) -{ - struct cs53l32a_state *state = to_state(sd); - u8 v = cs53l32a_read(sd, 0x01); - - v4l2_info(sd, "Input: %d\n", (v >> 4) & 3); - v4l2_ctrl_handler_log_status(&state->hdl, sd->name); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_ctrl_ops cs53l32a_ctrl_ops = { - .s_ctrl = cs53l32a_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops cs53l32a_core_ops = { - .log_status = cs53l32a_log_status, - .g_chip_ident = cs53l32a_g_chip_ident, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, -}; - -static const struct v4l2_subdev_audio_ops cs53l32a_audio_ops = { - .s_routing = cs53l32a_s_routing, -}; - -static const struct v4l2_subdev_ops cs53l32a_ops = { - .core = &cs53l32a_core_ops, - .audio = &cs53l32a_audio_ops, -}; - -/* ----------------------------------------------------------------------- */ - -/* i2c implementation */ - -/* - * Generic i2c probe - * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' - */ - -static int cs53l32a_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct cs53l32a_state *state; - struct v4l2_subdev *sd; - int i; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - if (!id) - strlcpy(client->name, "cs53l32a", sizeof(client->name)); - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - state = kzalloc(sizeof(struct cs53l32a_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &cs53l32a_ops); - - for (i = 1; i <= 7; i++) { - u8 v = cs53l32a_read(sd, i); - - v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v); - } - - v4l2_ctrl_handler_init(&state->hdl, 2); - v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops, - V4L2_CID_AUDIO_VOLUME, -96, 12, 1, 0); - v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops, - V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); - sd->ctrl_handler = &state->hdl; - if (state->hdl.error) { - int err = state->hdl.error; - - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return err; - } - - /* Set cs53l32a internal register for Adaptec 2010/2410 setup */ - - cs53l32a_write(sd, 0x01, 0x21); - cs53l32a_write(sd, 0x02, 0x29); - cs53l32a_write(sd, 0x03, 0x30); - cs53l32a_write(sd, 0x04, 0x00); - cs53l32a_write(sd, 0x05, 0x00); - cs53l32a_write(sd, 0x06, 0x00); - cs53l32a_write(sd, 0x07, 0x00); - - /* Display results, should be 0x21,0x29,0x30,0x00,0x00,0x00,0x00 */ - - for (i = 1; i <= 7; i++) { - u8 v = cs53l32a_read(sd, i); - - v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v); - } - return 0; -} - -static int cs53l32a_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct cs53l32a_state *state = to_state(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return 0; -} - -static const struct i2c_device_id cs53l32a_id[] = { - { "cs53l32a", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, cs53l32a_id); - -static struct i2c_driver cs53l32a_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "cs53l32a", - }, - .probe = cs53l32a_probe, - .remove = cs53l32a_remove, - .id_table = cs53l32a_id, -}; - -module_i2c_driver(cs53l32a_driver); diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c deleted file mode 100644 index 103ef6bad2e..00000000000 --- a/drivers/media/video/cx2341x.c +++ /dev/null @@ -1,1726 +0,0 @@ -/* - * cx2341x - generic code for cx23415/6/8 based devices - * - * Copyright (C) 2006 Hans Verkuil - * - * 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 -#include -#include -#include -#include -#include - -#include -#include -#include - -MODULE_DESCRIPTION("cx23415/6/8 driver"); -MODULE_AUTHOR("Hans Verkuil"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - -/********************** COMMON CODE *********************/ - -/* definitions for audio properties bits 29-28 */ -#define CX2341X_AUDIO_ENCODING_METHOD_MPEG 0 -#define CX2341X_AUDIO_ENCODING_METHOD_AC3 1 -#define CX2341X_AUDIO_ENCODING_METHOD_LPCM 2 - -static const char *cx2341x_get_name(u32 id) -{ - switch (id) { - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - return "Spatial Filter Mode"; - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: - return "Spatial Filter"; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - return "Spatial Luma Filter Type"; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: - return "Spatial Chroma Filter Type"; - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - return "Temporal Filter Mode"; - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: - return "Temporal Filter"; - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - return "Median Filter Type"; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: - return "Median Luma Filter Maximum"; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: - return "Median Luma Filter Minimum"; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: - return "Median Chroma Filter Maximum"; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: - return "Median Chroma Filter Minimum"; - case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: - return "Insert Navigation Packets"; - } - return NULL; -} - -static const char **cx2341x_get_menu(u32 id) -{ - static const char *cx2341x_video_spatial_filter_mode_menu[] = { - "Manual", - "Auto", - NULL - }; - - static const char *cx2341x_video_luma_spatial_filter_type_menu[] = { - "Off", - "1D Horizontal", - "1D Vertical", - "2D H/V Separable", - "2D Symmetric non-separable", - NULL - }; - - static const char *cx2341x_video_chroma_spatial_filter_type_menu[] = { - "Off", - "1D Horizontal", - NULL - }; - - static const char *cx2341x_video_temporal_filter_mode_menu[] = { - "Manual", - "Auto", - NULL - }; - - static const char *cx2341x_video_median_filter_type_menu[] = { - "Off", - "Horizontal", - "Vertical", - "Horizontal/Vertical", - "Diagonal", - NULL - }; - - switch (id) { - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - return cx2341x_video_spatial_filter_mode_menu; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - return cx2341x_video_luma_spatial_filter_type_menu; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: - return cx2341x_video_chroma_spatial_filter_type_menu; - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - return cx2341x_video_temporal_filter_mode_menu; - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - return cx2341x_video_median_filter_type_menu; - } - return NULL; -} - -static void cx2341x_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, - s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags) -{ - *name = cx2341x_get_name(id); - *flags = 0; - - switch (id) { - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - *type = V4L2_CTRL_TYPE_MENU; - *min = 0; - *step = 0; - break; - case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: - *type = V4L2_CTRL_TYPE_BOOLEAN; - *min = 0; - *max = *step = 1; - break; - default: - *type = V4L2_CTRL_TYPE_INTEGER; - break; - } - switch (id) { - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - *flags |= V4L2_CTRL_FLAG_UPDATE; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: - *flags |= V4L2_CTRL_FLAG_SLIDER; - break; - case V4L2_CID_MPEG_VIDEO_ENCODING: - *flags |= V4L2_CTRL_FLAG_READ_ONLY; - break; - } -} - - -/********************** OLD CODE *********************/ - -/* Must be sorted from low to high control ID! */ -const u32 cx2341x_mpeg_ctrls[] = { - V4L2_CID_MPEG_CLASS, - V4L2_CID_MPEG_STREAM_TYPE, - V4L2_CID_MPEG_STREAM_VBI_FMT, - V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, - V4L2_CID_MPEG_AUDIO_ENCODING, - V4L2_CID_MPEG_AUDIO_L2_BITRATE, - V4L2_CID_MPEG_AUDIO_MODE, - V4L2_CID_MPEG_AUDIO_MODE_EXTENSION, - V4L2_CID_MPEG_AUDIO_EMPHASIS, - V4L2_CID_MPEG_AUDIO_CRC, - V4L2_CID_MPEG_AUDIO_MUTE, - V4L2_CID_MPEG_AUDIO_AC3_BITRATE, - V4L2_CID_MPEG_VIDEO_ENCODING, - V4L2_CID_MPEG_VIDEO_ASPECT, - V4L2_CID_MPEG_VIDEO_B_FRAMES, - V4L2_CID_MPEG_VIDEO_GOP_SIZE, - V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, - V4L2_CID_MPEG_VIDEO_BITRATE_MODE, - V4L2_CID_MPEG_VIDEO_BITRATE, - V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, - V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION, - V4L2_CID_MPEG_VIDEO_MUTE, - V4L2_CID_MPEG_VIDEO_MUTE_YUV, - V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE, - V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER, - V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE, - V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE, - V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE, - V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER, - V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE, - V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM, - V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP, - V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM, - V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP, - V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS, - 0 -}; -EXPORT_SYMBOL(cx2341x_mpeg_ctrls); - -static const struct cx2341x_mpeg_params default_params = { - /* misc */ - .capabilities = 0, - .port = CX2341X_PORT_MEMORY, - .width = 720, - .height = 480, - .is_50hz = 0, - - /* stream */ - .stream_type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS, - .stream_vbi_fmt = V4L2_MPEG_STREAM_VBI_FMT_NONE, - .stream_insert_nav_packets = 0, - - /* audio */ - .audio_sampling_freq = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, - .audio_encoding = V4L2_MPEG_AUDIO_ENCODING_LAYER_2, - .audio_l2_bitrate = V4L2_MPEG_AUDIO_L2_BITRATE_224K, - .audio_ac3_bitrate = V4L2_MPEG_AUDIO_AC3_BITRATE_224K, - .audio_mode = V4L2_MPEG_AUDIO_MODE_STEREO, - .audio_mode_extension = V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4, - .audio_emphasis = V4L2_MPEG_AUDIO_EMPHASIS_NONE, - .audio_crc = V4L2_MPEG_AUDIO_CRC_NONE, - .audio_mute = 0, - - /* video */ - .video_encoding = V4L2_MPEG_VIDEO_ENCODING_MPEG_2, - .video_aspect = V4L2_MPEG_VIDEO_ASPECT_4x3, - .video_b_frames = 2, - .video_gop_size = 12, - .video_gop_closure = 1, - .video_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, - .video_bitrate = 6000000, - .video_bitrate_peak = 8000000, - .video_temporal_decimation = 0, - .video_mute = 0, - .video_mute_yuv = 0x008080, /* YCbCr value for black */ - - /* encoding filters */ - .video_spatial_filter_mode = - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, - .video_spatial_filter = 0, - .video_luma_spatial_filter_type = - V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR, - .video_chroma_spatial_filter_type = - V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, - .video_temporal_filter_mode = - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL, - .video_temporal_filter = 8, - .video_median_filter_type = - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF, - .video_luma_median_filter_top = 255, - .video_luma_median_filter_bottom = 0, - .video_chroma_median_filter_top = 255, - .video_chroma_median_filter_bottom = 0, -}; -/* Map the control ID to the correct field in the cx2341x_mpeg_params - struct. Return -EINVAL if the ID is unknown, else return 0. */ -static int cx2341x_get_ctrl(const struct cx2341x_mpeg_params *params, - struct v4l2_ext_control *ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: - ctrl->value = params->audio_sampling_freq; - break; - case V4L2_CID_MPEG_AUDIO_ENCODING: - ctrl->value = params->audio_encoding; - break; - case V4L2_CID_MPEG_AUDIO_L2_BITRATE: - ctrl->value = params->audio_l2_bitrate; - break; - case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: - ctrl->value = params->audio_ac3_bitrate; - break; - case V4L2_CID_MPEG_AUDIO_MODE: - ctrl->value = params->audio_mode; - break; - case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: - ctrl->value = params->audio_mode_extension; - break; - case V4L2_CID_MPEG_AUDIO_EMPHASIS: - ctrl->value = params->audio_emphasis; - break; - case V4L2_CID_MPEG_AUDIO_CRC: - ctrl->value = params->audio_crc; - break; - case V4L2_CID_MPEG_AUDIO_MUTE: - ctrl->value = params->audio_mute; - break; - case V4L2_CID_MPEG_VIDEO_ENCODING: - ctrl->value = params->video_encoding; - break; - case V4L2_CID_MPEG_VIDEO_ASPECT: - ctrl->value = params->video_aspect; - break; - case V4L2_CID_MPEG_VIDEO_B_FRAMES: - ctrl->value = params->video_b_frames; - break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - ctrl->value = params->video_gop_size; - break; - case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: - ctrl->value = params->video_gop_closure; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - ctrl->value = params->video_bitrate_mode; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE: - ctrl->value = params->video_bitrate; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - ctrl->value = params->video_bitrate_peak; - break; - case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: - ctrl->value = params->video_temporal_decimation; - break; - case V4L2_CID_MPEG_VIDEO_MUTE: - ctrl->value = params->video_mute; - break; - case V4L2_CID_MPEG_VIDEO_MUTE_YUV: - ctrl->value = params->video_mute_yuv; - break; - case V4L2_CID_MPEG_STREAM_TYPE: - ctrl->value = params->stream_type; - break; - case V4L2_CID_MPEG_STREAM_VBI_FMT: - ctrl->value = params->stream_vbi_fmt; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - ctrl->value = params->video_spatial_filter_mode; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: - ctrl->value = params->video_spatial_filter; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - ctrl->value = params->video_luma_spatial_filter_type; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: - ctrl->value = params->video_chroma_spatial_filter_type; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - ctrl->value = params->video_temporal_filter_mode; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: - ctrl->value = params->video_temporal_filter; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - ctrl->value = params->video_median_filter_type; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: - ctrl->value = params->video_luma_median_filter_top; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: - ctrl->value = params->video_luma_median_filter_bottom; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: - ctrl->value = params->video_chroma_median_filter_top; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: - ctrl->value = params->video_chroma_median_filter_bottom; - break; - case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: - ctrl->value = params->stream_insert_nav_packets; - break; - default: - return -EINVAL; - } - return 0; -} - -/* Map the control ID to the correct field in the cx2341x_mpeg_params - struct. Return -EINVAL if the ID is unknown, else return 0. */ -static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params, int busy, - struct v4l2_ext_control *ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: - if (busy) - return -EBUSY; - params->audio_sampling_freq = ctrl->value; - break; - case V4L2_CID_MPEG_AUDIO_ENCODING: - if (busy) - return -EBUSY; - if (params->capabilities & CX2341X_CAP_HAS_AC3) - if (ctrl->value != V4L2_MPEG_AUDIO_ENCODING_LAYER_2 && - ctrl->value != V4L2_MPEG_AUDIO_ENCODING_AC3) - return -ERANGE; - params->audio_encoding = ctrl->value; - break; - case V4L2_CID_MPEG_AUDIO_L2_BITRATE: - if (busy) - return -EBUSY; - params->audio_l2_bitrate = ctrl->value; - break; - case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: - if (busy) - return -EBUSY; - if (!(params->capabilities & CX2341X_CAP_HAS_AC3)) - return -EINVAL; - params->audio_ac3_bitrate = ctrl->value; - break; - case V4L2_CID_MPEG_AUDIO_MODE: - params->audio_mode = ctrl->value; - break; - case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: - params->audio_mode_extension = ctrl->value; - break; - case V4L2_CID_MPEG_AUDIO_EMPHASIS: - params->audio_emphasis = ctrl->value; - break; - case V4L2_CID_MPEG_AUDIO_CRC: - params->audio_crc = ctrl->value; - break; - case V4L2_CID_MPEG_AUDIO_MUTE: - params->audio_mute = ctrl->value; - break; - case V4L2_CID_MPEG_VIDEO_ASPECT: - params->video_aspect = ctrl->value; - break; - case V4L2_CID_MPEG_VIDEO_B_FRAMES: { - int b = ctrl->value + 1; - int gop = params->video_gop_size; - params->video_b_frames = ctrl->value; - params->video_gop_size = b * ((gop + b - 1) / b); - /* Max GOP size = 34 */ - while (params->video_gop_size > 34) - params->video_gop_size -= b; - break; - } - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: { - int b = params->video_b_frames + 1; - int gop = ctrl->value; - params->video_gop_size = b * ((gop + b - 1) / b); - /* Max GOP size = 34 */ - while (params->video_gop_size > 34) - params->video_gop_size -= b; - ctrl->value = params->video_gop_size; - break; - } - case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: - params->video_gop_closure = ctrl->value; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - if (busy) - return -EBUSY; - /* MPEG-1 only allows CBR */ - if (params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1 && - ctrl->value != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) - return -EINVAL; - params->video_bitrate_mode = ctrl->value; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE: - if (busy) - return -EBUSY; - params->video_bitrate = ctrl->value; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - if (busy) - return -EBUSY; - params->video_bitrate_peak = ctrl->value; - break; - case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: - params->video_temporal_decimation = ctrl->value; - break; - case V4L2_CID_MPEG_VIDEO_MUTE: - params->video_mute = (ctrl->value != 0); - break; - case V4L2_CID_MPEG_VIDEO_MUTE_YUV: - params->video_mute_yuv = ctrl->value; - break; - case V4L2_CID_MPEG_STREAM_TYPE: - if (busy) - return -EBUSY; - params->stream_type = ctrl->value; - params->video_encoding = - (params->stream_type == V4L2_MPEG_STREAM_TYPE_MPEG1_SS || - params->stream_type == V4L2_MPEG_STREAM_TYPE_MPEG1_VCD) ? - V4L2_MPEG_VIDEO_ENCODING_MPEG_1 : - V4L2_MPEG_VIDEO_ENCODING_MPEG_2; - if (params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) - /* MPEG-1 implies CBR */ - params->video_bitrate_mode = - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR; - break; - case V4L2_CID_MPEG_STREAM_VBI_FMT: - params->stream_vbi_fmt = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - params->video_spatial_filter_mode = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: - params->video_spatial_filter = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - params->video_luma_spatial_filter_type = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: - params->video_chroma_spatial_filter_type = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - params->video_temporal_filter_mode = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: - params->video_temporal_filter = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - params->video_median_filter_type = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: - params->video_luma_median_filter_top = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: - params->video_luma_median_filter_bottom = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: - params->video_chroma_median_filter_top = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: - params->video_chroma_median_filter_bottom = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: - params->stream_insert_nav_packets = ctrl->value; - break; - default: - return -EINVAL; - } - return 0; -} - -static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl, - s32 min, s32 max, s32 step, s32 def) -{ - const char *name; - - switch (qctrl->id) { - /* MPEG controls */ - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: - case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: - cx2341x_ctrl_fill(qctrl->id, &name, &qctrl->type, - &min, &max, &step, &def, &qctrl->flags); - qctrl->minimum = min; - qctrl->maximum = max; - qctrl->step = step; - qctrl->default_value = def; - qctrl->reserved[0] = qctrl->reserved[1] = 0; - strlcpy(qctrl->name, name, sizeof(qctrl->name)); - return 0; - - default: - return v4l2_ctrl_query_fill(qctrl, min, max, step, def); - } -} - -int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params, - struct v4l2_queryctrl *qctrl) -{ - int err; - - switch (qctrl->id) { - case V4L2_CID_MPEG_CLASS: - return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0); - case V4L2_CID_MPEG_STREAM_TYPE: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_STREAM_TYPE_MPEG2_PS, - V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, 1, - V4L2_MPEG_STREAM_TYPE_MPEG2_PS); - - case V4L2_CID_MPEG_STREAM_VBI_FMT: - if (params->capabilities & CX2341X_CAP_HAS_SLICED_VBI) - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_STREAM_VBI_FMT_NONE, - V4L2_MPEG_STREAM_VBI_FMT_IVTV, 1, - V4L2_MPEG_STREAM_VBI_FMT_NONE); - return cx2341x_ctrl_query_fill(qctrl, - V4L2_MPEG_STREAM_VBI_FMT_NONE, - V4L2_MPEG_STREAM_VBI_FMT_NONE, 1, - default_params.stream_vbi_fmt); - - case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100, - V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, 1, - V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000); - - case V4L2_CID_MPEG_AUDIO_ENCODING: - if (params->capabilities & CX2341X_CAP_HAS_AC3) { - /* - * The state of L2 & AC3 bitrate controls can change - * when this control changes, but v4l2_ctrl_query_fill() - * already sets V4L2_CTRL_FLAG_UPDATE for - * V4L2_CID_MPEG_AUDIO_ENCODING, so we don't here. - */ - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_ENCODING_LAYER_2, - V4L2_MPEG_AUDIO_ENCODING_AC3, 1, - default_params.audio_encoding); - } - - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_ENCODING_LAYER_2, - V4L2_MPEG_AUDIO_ENCODING_LAYER_2, 1, - default_params.audio_encoding); - - case V4L2_CID_MPEG_AUDIO_L2_BITRATE: - err = v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_L2_BITRATE_192K, - V4L2_MPEG_AUDIO_L2_BITRATE_384K, 1, - default_params.audio_l2_bitrate); - if (err) - return err; - if (params->capabilities & CX2341X_CAP_HAS_AC3 && - params->audio_encoding != V4L2_MPEG_AUDIO_ENCODING_LAYER_2) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; - - case V4L2_CID_MPEG_AUDIO_MODE: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_MODE_STEREO, - V4L2_MPEG_AUDIO_MODE_MONO, 1, - V4L2_MPEG_AUDIO_MODE_STEREO); - - case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: - err = v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4, - V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, 1, - V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4); - if (err == 0 && - params->audio_mode != V4L2_MPEG_AUDIO_MODE_JOINT_STEREO) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return err; - - case V4L2_CID_MPEG_AUDIO_EMPHASIS: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_EMPHASIS_NONE, - V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, 1, - V4L2_MPEG_AUDIO_EMPHASIS_NONE); - - case V4L2_CID_MPEG_AUDIO_CRC: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_CRC_NONE, - V4L2_MPEG_AUDIO_CRC_CRC16, 1, - V4L2_MPEG_AUDIO_CRC_NONE); - - case V4L2_CID_MPEG_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); - - case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: - err = v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_AC3_BITRATE_48K, - V4L2_MPEG_AUDIO_AC3_BITRATE_448K, 1, - default_params.audio_ac3_bitrate); - if (err) - return err; - if (params->capabilities & CX2341X_CAP_HAS_AC3) { - if (params->audio_encoding != - V4L2_MPEG_AUDIO_ENCODING_AC3) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - } else - qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; - return 0; - - case V4L2_CID_MPEG_VIDEO_ENCODING: - /* this setting is read-only for the cx2341x since the - V4L2_CID_MPEG_STREAM_TYPE really determines the - MPEG-1/2 setting */ - err = v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_VIDEO_ENCODING_MPEG_1, - V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 1, - V4L2_MPEG_VIDEO_ENCODING_MPEG_2); - if (err == 0) - qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - return err; - - case V4L2_CID_MPEG_VIDEO_ASPECT: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_VIDEO_ASPECT_1x1, - V4L2_MPEG_VIDEO_ASPECT_221x100, 1, - V4L2_MPEG_VIDEO_ASPECT_4x3); - - case V4L2_CID_MPEG_VIDEO_B_FRAMES: - return v4l2_ctrl_query_fill(qctrl, 0, 33, 1, 2); - - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - return v4l2_ctrl_query_fill(qctrl, 1, 34, 1, - params->is_50hz ? 12 : 15); - - case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: - return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 1); - - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - err = v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1, - V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); - if (err == 0 && - params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return err; - - case V4L2_CID_MPEG_VIDEO_BITRATE: - return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 6000000); - - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - err = v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 8000000); - if (err == 0 && - params->video_bitrate_mode == - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return err; - - case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: - return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 0); - - case V4L2_CID_MPEG_VIDEO_MUTE: - return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); - - case V4L2_CID_MPEG_VIDEO_MUTE_YUV: /* Init YUV (really YCbCr) to black */ - return v4l2_ctrl_query_fill(qctrl, 0, 0xffffff, 1, 0x008080); - - /* CX23415/6 specific */ - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - return cx2341x_ctrl_query_fill(qctrl, - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 1, - default_params.video_spatial_filter_mode); - - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: - cx2341x_ctrl_query_fill(qctrl, 0, 15, 1, - default_params.video_spatial_filter); - qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; - if (params->video_spatial_filter_mode == - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; - - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - cx2341x_ctrl_query_fill(qctrl, - V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF, - V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE, - 1, - default_params.video_luma_spatial_filter_type); - if (params->video_spatial_filter_mode == - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; - - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: - cx2341x_ctrl_query_fill(qctrl, - V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF, - V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, - 1, - default_params.video_chroma_spatial_filter_type); - if (params->video_spatial_filter_mode == - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; - - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - return cx2341x_ctrl_query_fill(qctrl, - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL, - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO, 1, - default_params.video_temporal_filter_mode); - - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: - cx2341x_ctrl_query_fill(qctrl, 0, 31, 1, - default_params.video_temporal_filter); - qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; - if (params->video_temporal_filter_mode == - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; - - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - return cx2341x_ctrl_query_fill(qctrl, - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF, - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG, 1, - default_params.video_median_filter_type); - - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: - cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, - default_params.video_luma_median_filter_top); - qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; - if (params->video_median_filter_type == - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; - - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: - cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, - default_params.video_luma_median_filter_bottom); - qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; - if (params->video_median_filter_type == - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; - - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: - cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, - default_params.video_chroma_median_filter_top); - qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; - if (params->video_median_filter_type == - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; - - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: - cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, - default_params.video_chroma_median_filter_bottom); - qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; - if (params->video_median_filter_type == - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; - - case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: - return cx2341x_ctrl_query_fill(qctrl, 0, 1, 1, - default_params.stream_insert_nav_packets); - - default: - return -EINVAL; - - } -} -EXPORT_SYMBOL(cx2341x_ctrl_query); - -const char * const *cx2341x_ctrl_get_menu(const struct cx2341x_mpeg_params *p, u32 id) -{ - static const char * const mpeg_stream_type_without_ts[] = { - "MPEG-2 Program Stream", - "", - "MPEG-1 System Stream", - "MPEG-2 DVD-compatible Stream", - "MPEG-1 VCD-compatible Stream", - "MPEG-2 SVCD-compatible Stream", - NULL - }; - - static const char *mpeg_stream_type_with_ts[] = { - "MPEG-2 Program Stream", - "MPEG-2 Transport Stream", - "MPEG-1 System Stream", - "MPEG-2 DVD-compatible Stream", - "MPEG-1 VCD-compatible Stream", - "MPEG-2 SVCD-compatible Stream", - NULL - }; - - static const char *mpeg_audio_encoding_l2_ac3[] = { - "", - "MPEG-1/2 Layer II", - "", - "", - "AC-3", - NULL - }; - - switch (id) { - case V4L2_CID_MPEG_STREAM_TYPE: - return (p->capabilities & CX2341X_CAP_HAS_TS) ? - mpeg_stream_type_with_ts : mpeg_stream_type_without_ts; - case V4L2_CID_MPEG_AUDIO_ENCODING: - return (p->capabilities & CX2341X_CAP_HAS_AC3) ? - mpeg_audio_encoding_l2_ac3 : v4l2_ctrl_get_menu(id); - case V4L2_CID_MPEG_AUDIO_L1_BITRATE: - case V4L2_CID_MPEG_AUDIO_L3_BITRATE: - return NULL; - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - return cx2341x_get_menu(id); - default: - return v4l2_ctrl_get_menu(id); - } -} -EXPORT_SYMBOL(cx2341x_ctrl_get_menu); - -static void cx2341x_calc_audio_properties(struct cx2341x_mpeg_params *params) -{ - params->audio_properties = - (params->audio_sampling_freq << 0) | - (params->audio_mode << 8) | - (params->audio_mode_extension << 10) | - (((params->audio_emphasis == V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17) - ? 3 : params->audio_emphasis) << 12) | - (params->audio_crc << 14); - - if ((params->capabilities & CX2341X_CAP_HAS_AC3) && - params->audio_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3) { - params->audio_properties |= - /* Not sure if this MPEG Layer II setting is required */ - ((3 - V4L2_MPEG_AUDIO_ENCODING_LAYER_2) << 2) | - (params->audio_ac3_bitrate << 4) | - (CX2341X_AUDIO_ENCODING_METHOD_AC3 << 28); - } else { - /* Assuming MPEG Layer II */ - params->audio_properties |= - ((3 - params->audio_encoding) << 2) | - ((1 + params->audio_l2_bitrate) << 4); - } -} - -int cx2341x_ext_ctrls(struct cx2341x_mpeg_params *params, int busy, - struct v4l2_ext_controls *ctrls, unsigned int cmd) -{ - int err = 0; - int i; - - if (cmd == VIDIOC_G_EXT_CTRLS) { - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = ctrls->controls + i; - - err = cx2341x_get_ctrl(params, ctrl); - if (err) { - ctrls->error_idx = i; - break; - } - } - return err; - } - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = ctrls->controls + i; - struct v4l2_queryctrl qctrl; - const char * const *menu_items = NULL; - - qctrl.id = ctrl->id; - err = cx2341x_ctrl_query(params, &qctrl); - if (err) - break; - if (qctrl.type == V4L2_CTRL_TYPE_MENU) - menu_items = cx2341x_ctrl_get_menu(params, qctrl.id); - err = v4l2_ctrl_check(ctrl, &qctrl, menu_items); - if (err) - break; - err = cx2341x_set_ctrl(params, busy, ctrl); - if (err) - break; - } - if (err == 0 && - params->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && - params->video_bitrate_peak < params->video_bitrate) { - err = -ERANGE; - ctrls->error_idx = ctrls->count; - } - if (err) - ctrls->error_idx = i; - else - cx2341x_calc_audio_properties(params); - return err; -} -EXPORT_SYMBOL(cx2341x_ext_ctrls); - -void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p) -{ - *p = default_params; - cx2341x_calc_audio_properties(p); -} -EXPORT_SYMBOL(cx2341x_fill_defaults); - -static int cx2341x_api(void *priv, cx2341x_mbox_func func, - u32 cmd, int args, ...) -{ - u32 data[CX2341X_MBOX_MAX_DATA]; - va_list vargs; - int i; - - va_start(vargs, args); - - for (i = 0; i < args; i++) - data[i] = va_arg(vargs, int); - va_end(vargs); - return func(priv, cmd, args, 0, data); -} - -#define NEQ(field) (old->field != new->field) - -int cx2341x_update(void *priv, cx2341x_mbox_func func, - const struct cx2341x_mpeg_params *old, - const struct cx2341x_mpeg_params *new) -{ - static int mpeg_stream_type[] = { - 0, /* MPEG-2 PS */ - 1, /* MPEG-2 TS */ - 2, /* MPEG-1 SS */ - 14, /* DVD */ - 11, /* VCD */ - 12, /* SVCD */ - }; - - int err = 0; - int force = (old == NULL); - u16 temporal = new->video_temporal_filter; - - cx2341x_api(priv, func, CX2341X_ENC_SET_OUTPUT_PORT, 2, new->port, 0); - - if (force || NEQ(is_50hz)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_RATE, 1, - new->is_50hz); - if (err) return err; - } - - if (force || NEQ(width) || NEQ(height) || NEQ(video_encoding)) { - u16 w = new->width; - u16 h = new->height; - - if (new->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) { - w /= 2; - h /= 2; - } - err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_SIZE, 2, - h, w); - if (err) return err; - } - if (force || NEQ(stream_type)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_STREAM_TYPE, 1, - mpeg_stream_type[new->stream_type]); - if (err) return err; - } - if (force || NEQ(video_aspect)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_ASPECT_RATIO, 1, - 1 + new->video_aspect); - if (err) return err; - } - if (force || NEQ(video_b_frames) || NEQ(video_gop_size)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_GOP_PROPERTIES, 2, - new->video_gop_size, new->video_b_frames + 1); - if (err) return err; - } - if (force || NEQ(video_gop_closure)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_GOP_CLOSURE, 1, - new->video_gop_closure); - if (err) return err; - } - if (force || NEQ(audio_properties)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_AUDIO_PROPERTIES, - 1, new->audio_properties); - if (err) return err; - } - if (force || NEQ(audio_mute)) { - err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_AUDIO, 1, - new->audio_mute); - if (err) return err; - } - if (force || NEQ(video_bitrate_mode) || NEQ(video_bitrate) || - NEQ(video_bitrate_peak)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_BIT_RATE, 5, - new->video_bitrate_mode, new->video_bitrate, - new->video_bitrate_peak / 400, 0, 0); - if (err) return err; - } - if (force || NEQ(video_spatial_filter_mode) || - NEQ(video_temporal_filter_mode) || - NEQ(video_median_filter_type)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_MODE, - 2, new->video_spatial_filter_mode | - (new->video_temporal_filter_mode << 1), - new->video_median_filter_type); - if (err) return err; - } - if (force || NEQ(video_luma_median_filter_bottom) || - NEQ(video_luma_median_filter_top) || - NEQ(video_chroma_median_filter_bottom) || - NEQ(video_chroma_median_filter_top)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_CORING_LEVELS, 4, - new->video_luma_median_filter_bottom, - new->video_luma_median_filter_top, - new->video_chroma_median_filter_bottom, - new->video_chroma_median_filter_top); - if (err) return err; - } - if (force || NEQ(video_luma_spatial_filter_type) || - NEQ(video_chroma_spatial_filter_type)) { - err = cx2341x_api(priv, func, - CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, - 2, new->video_luma_spatial_filter_type, - new->video_chroma_spatial_filter_type); - if (err) return err; - } - if (force || NEQ(video_spatial_filter) || - old->video_temporal_filter != temporal) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_PROPS, - 2, new->video_spatial_filter, temporal); - if (err) return err; - } - if (force || NEQ(video_temporal_decimation)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_DROP_RATE, - 1, new->video_temporal_decimation); - if (err) return err; - } - if (force || NEQ(video_mute) || - (new->video_mute && NEQ(video_mute_yuv))) { - err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_VIDEO, 1, - new->video_mute | (new->video_mute_yuv << 8)); - if (err) return err; - } - if (force || NEQ(stream_insert_nav_packets)) { - err = cx2341x_api(priv, func, CX2341X_ENC_MISC, 2, - 7, new->stream_insert_nav_packets); - if (err) return err; - } - return 0; -} -EXPORT_SYMBOL(cx2341x_update); - -static const char *cx2341x_menu_item(const struct cx2341x_mpeg_params *p, u32 id) -{ - const char * const *menu = cx2341x_ctrl_get_menu(p, id); - struct v4l2_ext_control ctrl; - - if (menu == NULL) - goto invalid; - ctrl.id = id; - if (cx2341x_get_ctrl(p, &ctrl)) - goto invalid; - while (ctrl.value-- && *menu) menu++; - if (*menu == NULL) - goto invalid; - return *menu; - -invalid: - return ""; -} - -void cx2341x_log_status(const struct cx2341x_mpeg_params *p, const char *prefix) -{ - int is_mpeg1 = p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1; - - /* Stream */ - printk(KERN_INFO "%s: Stream: %s", - prefix, - cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_TYPE)); - if (p->stream_insert_nav_packets) - printk(" (with navigation packets)"); - printk("\n"); - printk(KERN_INFO "%s: VBI Format: %s\n", - prefix, - cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_VBI_FMT)); - - /* Video */ - printk(KERN_INFO "%s: Video: %dx%d, %d fps%s\n", - prefix, - p->width / (is_mpeg1 ? 2 : 1), p->height / (is_mpeg1 ? 2 : 1), - p->is_50hz ? 25 : 30, - (p->video_mute) ? " (muted)" : ""); - printk(KERN_INFO "%s: Video: %s, %s, %s, %d", - prefix, - cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ENCODING), - cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ASPECT), - cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_BITRATE_MODE), - p->video_bitrate); - if (p->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) - printk(", Peak %d", p->video_bitrate_peak); - printk("\n"); - printk(KERN_INFO - "%s: Video: GOP Size %d, %d B-Frames, %sGOP Closure\n", - prefix, - p->video_gop_size, p->video_b_frames, - p->video_gop_closure ? "" : "No "); - if (p->video_temporal_decimation) - printk(KERN_INFO "%s: Video: Temporal Decimation %d\n", - prefix, p->video_temporal_decimation); - - /* Audio */ - printk(KERN_INFO "%s: Audio: %s, %s, %s, %s%s", - prefix, - cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ), - cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_ENCODING), - cx2341x_menu_item(p, - p->audio_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3 - ? V4L2_CID_MPEG_AUDIO_AC3_BITRATE - : V4L2_CID_MPEG_AUDIO_L2_BITRATE), - cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE), - p->audio_mute ? " (muted)" : ""); - if (p->audio_mode == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO) - printk(", %s", cx2341x_menu_item(p, - V4L2_CID_MPEG_AUDIO_MODE_EXTENSION)); - printk(", %s, %s\n", - cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_EMPHASIS), - cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_CRC)); - - /* Encoding filters */ - printk(KERN_INFO "%s: Spatial Filter: %s, Luma %s, Chroma %s, %d\n", - prefix, - cx2341x_menu_item(p, - V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE), - cx2341x_menu_item(p, - V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE), - cx2341x_menu_item(p, - V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE), - p->video_spatial_filter); - - printk(KERN_INFO "%s: Temporal Filter: %s, %d\n", - prefix, - cx2341x_menu_item(p, - V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE), - p->video_temporal_filter); - printk(KERN_INFO - "%s: Median Filter: %s, Luma [%d, %d], Chroma [%d, %d]\n", - prefix, - cx2341x_menu_item(p, - V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE), - p->video_luma_median_filter_bottom, - p->video_luma_median_filter_top, - p->video_chroma_median_filter_bottom, - p->video_chroma_median_filter_top); -} -EXPORT_SYMBOL(cx2341x_log_status); - - - -/********************** NEW CODE *********************/ - -static inline struct cx2341x_handler *to_cxhdl(struct v4l2_ctrl *ctrl) -{ - return container_of(ctrl->handler, struct cx2341x_handler, hdl); -} - -static int cx2341x_hdl_api(struct cx2341x_handler *hdl, - u32 cmd, int args, ...) -{ - u32 data[CX2341X_MBOX_MAX_DATA]; - va_list vargs; - int i; - - va_start(vargs, args); - - for (i = 0; i < args; i++) - data[i] = va_arg(vargs, int); - va_end(vargs); - return hdl->func(hdl->priv, cmd, args, 0, data); -} - -/* ctrl->handler->lock is held, so it is safe to access cur.val */ -static inline int cx2341x_neq(struct v4l2_ctrl *ctrl) -{ - return ctrl && ctrl->val != ctrl->cur.val; -} - -static int cx2341x_try_ctrl(struct v4l2_ctrl *ctrl) -{ - struct cx2341x_handler *hdl = to_cxhdl(ctrl); - s32 val = ctrl->val; - - switch (ctrl->id) { - case V4L2_CID_MPEG_VIDEO_B_FRAMES: { - /* video gop cluster */ - int b = val + 1; - int gop = hdl->video_gop_size->val; - - gop = b * ((gop + b - 1) / b); - - /* Max GOP size = 34 */ - while (gop > 34) - gop -= b; - hdl->video_gop_size->val = gop; - break; - } - - case V4L2_CID_MPEG_STREAM_TYPE: - /* stream type cluster */ - hdl->video_encoding->val = - (hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_SS || - hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_VCD) ? - V4L2_MPEG_VIDEO_ENCODING_MPEG_1 : - V4L2_MPEG_VIDEO_ENCODING_MPEG_2; - if (hdl->video_encoding->val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) - /* MPEG-1 implies CBR */ - hdl->video_bitrate_mode->val = - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR; - /* peak bitrate shall be >= normal bitrate */ - if (hdl->video_bitrate_mode->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && - hdl->video_bitrate_peak->val < hdl->video_bitrate->val) - hdl->video_bitrate_peak->val = hdl->video_bitrate->val; - break; - } - return 0; -} - -static int cx2341x_s_ctrl(struct v4l2_ctrl *ctrl) -{ - static const int mpeg_stream_type[] = { - 0, /* MPEG-2 PS */ - 1, /* MPEG-2 TS */ - 2, /* MPEG-1 SS */ - 14, /* DVD */ - 11, /* VCD */ - 12, /* SVCD */ - }; - struct cx2341x_handler *hdl = to_cxhdl(ctrl); - s32 val = ctrl->val; - u32 props; - int err; - - switch (ctrl->id) { - case V4L2_CID_MPEG_STREAM_VBI_FMT: - if (hdl->ops && hdl->ops->s_stream_vbi_fmt) - return hdl->ops->s_stream_vbi_fmt(hdl, val); - return 0; - - case V4L2_CID_MPEG_VIDEO_ASPECT: - return cx2341x_hdl_api(hdl, - CX2341X_ENC_SET_ASPECT_RATIO, 1, val + 1); - - case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: - return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_CLOSURE, 1, val); - - case V4L2_CID_MPEG_AUDIO_MUTE: - return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_AUDIO, 1, val); - - case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: - return cx2341x_hdl_api(hdl, - CX2341X_ENC_SET_FRAME_DROP_RATE, 1, val); - - case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: - return cx2341x_hdl_api(hdl, CX2341X_ENC_MISC, 2, 7, val); - - case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: - /* audio properties cluster */ - props = (hdl->audio_sampling_freq->val << 0) | - (hdl->audio_mode->val << 8) | - (hdl->audio_mode_extension->val << 10) | - (hdl->audio_crc->val << 14); - if (hdl->audio_emphasis->val == V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17) - props |= 3 << 12; - else - props |= hdl->audio_emphasis->val << 12; - - if (hdl->audio_encoding->val == V4L2_MPEG_AUDIO_ENCODING_AC3) { - props |= -#if 1 - /* Not sure if this MPEG Layer II setting is required */ - ((3 - V4L2_MPEG_AUDIO_ENCODING_LAYER_2) << 2) | -#endif - (hdl->audio_ac3_bitrate->val << 4) | - (CX2341X_AUDIO_ENCODING_METHOD_AC3 << 28); - } else { - /* Assuming MPEG Layer II */ - props |= - ((3 - hdl->audio_encoding->val) << 2) | - ((1 + hdl->audio_l2_bitrate->val) << 4); - } - err = cx2341x_hdl_api(hdl, - CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, props); - if (err) - return err; - - hdl->audio_properties = props; - if (hdl->audio_ac3_bitrate) { - int is_ac3 = hdl->audio_encoding->val == - V4L2_MPEG_AUDIO_ENCODING_AC3; - - v4l2_ctrl_activate(hdl->audio_ac3_bitrate, is_ac3); - v4l2_ctrl_activate(hdl->audio_l2_bitrate, !is_ac3); - } - v4l2_ctrl_activate(hdl->audio_mode_extension, - hdl->audio_mode->val == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO); - if (cx2341x_neq(hdl->audio_sampling_freq) && - hdl->ops && hdl->ops->s_audio_sampling_freq) - return hdl->ops->s_audio_sampling_freq(hdl, hdl->audio_sampling_freq->val); - if (cx2341x_neq(hdl->audio_mode) && - hdl->ops && hdl->ops->s_audio_mode) - return hdl->ops->s_audio_mode(hdl, hdl->audio_mode->val); - return 0; - - case V4L2_CID_MPEG_VIDEO_B_FRAMES: - /* video gop cluster */ - return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_PROPERTIES, 2, - hdl->video_gop_size->val, - hdl->video_b_frames->val + 1); - - case V4L2_CID_MPEG_STREAM_TYPE: - /* stream type cluster */ - err = cx2341x_hdl_api(hdl, - CX2341X_ENC_SET_STREAM_TYPE, 1, mpeg_stream_type[val]); - if (err) - return err; - - err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_BIT_RATE, 5, - hdl->video_bitrate_mode->val, - hdl->video_bitrate->val, - hdl->video_bitrate_peak->val / 400, 0, 0); - if (err) - return err; - - v4l2_ctrl_activate(hdl->video_bitrate_mode, - hdl->video_encoding->val != V4L2_MPEG_VIDEO_ENCODING_MPEG_1); - v4l2_ctrl_activate(hdl->video_bitrate_peak, - hdl->video_bitrate_mode->val != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); - if (cx2341x_neq(hdl->video_encoding) && - hdl->ops && hdl->ops->s_video_encoding) - return hdl->ops->s_video_encoding(hdl, hdl->video_encoding->val); - return 0; - - case V4L2_CID_MPEG_VIDEO_MUTE: - /* video mute cluster */ - return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_VIDEO, 1, - hdl->video_mute->val | - (hdl->video_mute_yuv->val << 8)); - - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: { - int active_filter; - - /* video filter mode */ - err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_MODE, 2, - hdl->video_spatial_filter_mode->val | - (hdl->video_temporal_filter_mode->val << 1), - hdl->video_median_filter_type->val); - if (err) - return err; - - active_filter = hdl->video_spatial_filter_mode->val != - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO; - v4l2_ctrl_activate(hdl->video_spatial_filter, active_filter); - v4l2_ctrl_activate(hdl->video_luma_spatial_filter_type, active_filter); - v4l2_ctrl_activate(hdl->video_chroma_spatial_filter_type, active_filter); - active_filter = hdl->video_temporal_filter_mode->val != - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO; - v4l2_ctrl_activate(hdl->video_temporal_filter, active_filter); - active_filter = hdl->video_median_filter_type->val != - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF; - v4l2_ctrl_activate(hdl->video_luma_median_filter_bottom, active_filter); - v4l2_ctrl_activate(hdl->video_luma_median_filter_top, active_filter); - v4l2_ctrl_activate(hdl->video_chroma_median_filter_bottom, active_filter); - v4l2_ctrl_activate(hdl->video_chroma_median_filter_top, active_filter); - return 0; - } - - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - /* video filter type cluster */ - return cx2341x_hdl_api(hdl, - CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, 2, - hdl->video_luma_spatial_filter_type->val, - hdl->video_chroma_spatial_filter_type->val); - - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: - /* video filter cluster */ - return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_PROPS, 2, - hdl->video_spatial_filter->val, - hdl->video_temporal_filter->val); - - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: - /* video median cluster */ - return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_CORING_LEVELS, 4, - hdl->video_luma_median_filter_bottom->val, - hdl->video_luma_median_filter_top->val, - hdl->video_chroma_median_filter_bottom->val, - hdl->video_chroma_median_filter_top->val); - } - return -EINVAL; -} - -static const struct v4l2_ctrl_ops cx2341x_ops = { - .try_ctrl = cx2341x_try_ctrl, - .s_ctrl = cx2341x_s_ctrl, -}; - -static struct v4l2_ctrl *cx2341x_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, - u32 id, s32 min, s32 max, s32 step, s32 def) -{ - struct v4l2_ctrl_config cfg; - - cx2341x_ctrl_fill(id, &cfg.name, &cfg.type, &min, &max, &step, &def, &cfg.flags); - cfg.ops = &cx2341x_ops; - cfg.id = id; - cfg.min = min; - cfg.max = max; - cfg.def = def; - if (cfg.type == V4L2_CTRL_TYPE_MENU) { - cfg.step = 0; - cfg.menu_skip_mask = step; - cfg.qmenu = cx2341x_get_menu(id); - } else { - cfg.step = step; - cfg.menu_skip_mask = 0; - } - return v4l2_ctrl_new_custom(hdl, &cfg, NULL); -} - -static struct v4l2_ctrl *cx2341x_ctrl_new_std(struct v4l2_ctrl_handler *hdl, - u32 id, s32 min, s32 max, s32 step, s32 def) -{ - return v4l2_ctrl_new_std(hdl, &cx2341x_ops, id, min, max, step, def); -} - -static struct v4l2_ctrl *cx2341x_ctrl_new_menu(struct v4l2_ctrl_handler *hdl, - u32 id, s32 max, s32 mask, s32 def) -{ - return v4l2_ctrl_new_std_menu(hdl, &cx2341x_ops, id, max, mask, def); -} - -int cx2341x_handler_init(struct cx2341x_handler *cxhdl, - unsigned nr_of_controls_hint) -{ - struct v4l2_ctrl_handler *hdl = &cxhdl->hdl; - u32 caps = cxhdl->capabilities; - int has_sliced_vbi = caps & CX2341X_CAP_HAS_SLICED_VBI; - int has_ac3 = caps & CX2341X_CAP_HAS_AC3; - int has_ts = caps & CX2341X_CAP_HAS_TS; - - cxhdl->width = 720; - cxhdl->height = 480; - - v4l2_ctrl_handler_init(hdl, nr_of_controls_hint); - - /* Add controls in ascending control ID order for fastest - insertion time. */ - cxhdl->stream_type = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_STREAM_TYPE, - V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, has_ts ? 0 : 2, - V4L2_MPEG_STREAM_TYPE_MPEG2_PS); - cxhdl->stream_vbi_fmt = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_STREAM_VBI_FMT, - V4L2_MPEG_STREAM_VBI_FMT_IVTV, has_sliced_vbi ? 0 : 2, - V4L2_MPEG_STREAM_VBI_FMT_NONE); - cxhdl->audio_sampling_freq = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, - V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, 0, - V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000); - cxhdl->audio_encoding = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_AUDIO_ENCODING, - V4L2_MPEG_AUDIO_ENCODING_AC3, has_ac3 ? ~0x12 : ~0x2, - V4L2_MPEG_AUDIO_ENCODING_LAYER_2); - cxhdl->audio_l2_bitrate = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_AUDIO_L2_BITRATE, - V4L2_MPEG_AUDIO_L2_BITRATE_384K, 0x1ff, - V4L2_MPEG_AUDIO_L2_BITRATE_224K); - cxhdl->audio_mode = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_AUDIO_MODE, - V4L2_MPEG_AUDIO_MODE_MONO, 0, - V4L2_MPEG_AUDIO_MODE_STEREO); - cxhdl->audio_mode_extension = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_AUDIO_MODE_EXTENSION, - V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, 0, - V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4); - cxhdl->audio_emphasis = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_AUDIO_EMPHASIS, - V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, 0, - V4L2_MPEG_AUDIO_EMPHASIS_NONE); - cxhdl->audio_crc = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_AUDIO_CRC, - V4L2_MPEG_AUDIO_CRC_CRC16, 0, - V4L2_MPEG_AUDIO_CRC_NONE); - - cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_AUDIO_MUTE, 0, 1, 1, 0); - if (has_ac3) - cxhdl->audio_ac3_bitrate = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_AUDIO_AC3_BITRATE, - V4L2_MPEG_AUDIO_AC3_BITRATE_448K, 0x03, - V4L2_MPEG_AUDIO_AC3_BITRATE_224K); - cxhdl->video_encoding = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_VIDEO_ENCODING, - V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 0, - V4L2_MPEG_VIDEO_ENCODING_MPEG_2); - cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_VIDEO_ASPECT, - V4L2_MPEG_VIDEO_ASPECT_221x100, 0, - V4L2_MPEG_VIDEO_ASPECT_4x3); - cxhdl->video_b_frames = cx2341x_ctrl_new_std(hdl, - V4L2_CID_MPEG_VIDEO_B_FRAMES, 0, 33, 1, 2); - cxhdl->video_gop_size = cx2341x_ctrl_new_std(hdl, - V4L2_CID_MPEG_VIDEO_GOP_SIZE, - 1, 34, 1, cxhdl->is_50hz ? 12 : 15); - cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, 0, 1, 1, 1); - cxhdl->video_bitrate_mode = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_VIDEO_BITRATE_MODE, - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0, - V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); - cxhdl->video_bitrate = cx2341x_ctrl_new_std(hdl, - V4L2_CID_MPEG_VIDEO_BITRATE, - 0, 27000000, 1, 6000000); - cxhdl->video_bitrate_peak = cx2341x_ctrl_new_std(hdl, - V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, - 0, 27000000, 1, 8000000); - cx2341x_ctrl_new_std(hdl, - V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION, 0, 255, 1, 0); - cxhdl->video_mute = cx2341x_ctrl_new_std(hdl, - V4L2_CID_MPEG_VIDEO_MUTE, 0, 1, 1, 0); - cxhdl->video_mute_yuv = cx2341x_ctrl_new_std(hdl, - V4L2_CID_MPEG_VIDEO_MUTE_YUV, 0, 0xffffff, 1, 0x008080); - - /* CX23415/6 specific */ - cxhdl->video_spatial_filter_mode = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE, - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 0, - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL); - cxhdl->video_spatial_filter = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER, - 0, 15, 1, 0); - cxhdl->video_luma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE, - V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF, - V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE, - 0, - V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR); - cxhdl->video_chroma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE, - V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF, - V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, - 0, - V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR); - cxhdl->video_temporal_filter_mode = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE, - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL, - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO, - 0, - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL); - cxhdl->video_temporal_filter = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER, - 0, 31, 1, 8); - cxhdl->video_median_filter_type = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE, - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF, - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG, - 0, - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF); - cxhdl->video_luma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM, - 0, 255, 1, 0); - cxhdl->video_luma_median_filter_top = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP, - 0, 255, 1, 255); - cxhdl->video_chroma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM, - 0, 255, 1, 0); - cxhdl->video_chroma_median_filter_top = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP, - 0, 255, 1, 255); - cx2341x_ctrl_new_custom(hdl, V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS, - 0, 1, 1, 0); - - if (hdl->error) { - int err = hdl->error; - - v4l2_ctrl_handler_free(hdl); - return err; - } - - v4l2_ctrl_cluster(8, &cxhdl->audio_sampling_freq); - v4l2_ctrl_cluster(2, &cxhdl->video_b_frames); - v4l2_ctrl_cluster(5, &cxhdl->stream_type); - v4l2_ctrl_cluster(2, &cxhdl->video_mute); - v4l2_ctrl_cluster(3, &cxhdl->video_spatial_filter_mode); - v4l2_ctrl_cluster(2, &cxhdl->video_luma_spatial_filter_type); - v4l2_ctrl_cluster(2, &cxhdl->video_spatial_filter); - v4l2_ctrl_cluster(4, &cxhdl->video_luma_median_filter_top); - - return 0; -} -EXPORT_SYMBOL(cx2341x_handler_init); - -void cx2341x_handler_set_50hz(struct cx2341x_handler *cxhdl, int is_50hz) -{ - cxhdl->is_50hz = is_50hz; - cxhdl->video_gop_size->default_value = cxhdl->is_50hz ? 12 : 15; -} -EXPORT_SYMBOL(cx2341x_handler_set_50hz); - -int cx2341x_handler_setup(struct cx2341x_handler *cxhdl) -{ - int h = cxhdl->height; - int w = cxhdl->width; - int err; - - err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_OUTPUT_PORT, 2, cxhdl->port, 0); - if (err) - return err; - err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_RATE, 1, cxhdl->is_50hz); - if (err) - return err; - - if (v4l2_ctrl_g_ctrl(cxhdl->video_encoding) == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) { - w /= 2; - h /= 2; - } - err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_SIZE, 2, h, w); - if (err) - return err; - return v4l2_ctrl_handler_setup(&cxhdl->hdl); -} -EXPORT_SYMBOL(cx2341x_handler_setup); - -void cx2341x_handler_set_busy(struct cx2341x_handler *cxhdl, int busy) -{ - v4l2_ctrl_grab(cxhdl->audio_sampling_freq, busy); - v4l2_ctrl_grab(cxhdl->audio_encoding, busy); - v4l2_ctrl_grab(cxhdl->audio_l2_bitrate, busy); - v4l2_ctrl_grab(cxhdl->audio_ac3_bitrate, busy); - v4l2_ctrl_grab(cxhdl->stream_vbi_fmt, busy); - v4l2_ctrl_grab(cxhdl->stream_type, busy); - v4l2_ctrl_grab(cxhdl->video_bitrate_mode, busy); - v4l2_ctrl_grab(cxhdl->video_bitrate, busy); - v4l2_ctrl_grab(cxhdl->video_bitrate_peak, busy); -} -EXPORT_SYMBOL(cx2341x_handler_set_busy); diff --git a/drivers/media/video/cx25840/Kconfig b/drivers/media/video/cx25840/Kconfig deleted file mode 100644 index 451133ad41f..00000000000 --- a/drivers/media/video/cx25840/Kconfig +++ /dev/null @@ -1,8 +0,0 @@ -config VIDEO_CX25840 - tristate "Conexant CX2584x audio/video decoders" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Conexant CX2584x audio/video decoders. - - To compile this driver as a module, choose M here: the - module will be called cx25840 diff --git a/drivers/media/video/cx25840/Makefile b/drivers/media/video/cx25840/Makefile deleted file mode 100644 index dc40dde2e0c..00000000000 --- a/drivers/media/video/cx25840/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -cx25840-objs := cx25840-core.o cx25840-audio.o cx25840-firmware.o \ - cx25840-vbi.o cx25840-ir.o - -obj-$(CONFIG_VIDEO_CX25840) += cx25840.o - -ccflags-y += -Idrivers/media/video diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c deleted file mode 100644 index 34b96c7cfd6..00000000000 --- a/drivers/media/video/cx25840/cx25840-audio.c +++ /dev/null @@ -1,571 +0,0 @@ -/* cx25840 audio functions - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - - -#include -#include -#include -#include - -#include "cx25840-core.h" - -/* - * Note: The PLL and SRC parameters are based on a reference frequency that - * would ideally be: - * - * NTSC Color subcarrier freq * 8 = 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz - * - * However, it's not the exact reference frequency that matters, only that the - * firmware and modules that comprise the driver for a particular board all - * use the same value (close to the ideal value). - * - * Comments below will note which reference frequency is assumed for various - * parameters. They will usually be one of - * - * ref_freq = 28.636360 MHz - * or - * ref_freq = 28.636363 MHz - */ - -static int cx25840_set_audclk_freq(struct i2c_client *client, u32 freq) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - - if (state->aud_input != CX25840_AUDIO_SERIAL) { - switch (freq) { - case 32000: - /* - * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 - * AUX_PLL Integer = 0x06, AUX PLL Post Divider = 0x10 - */ - cx25840_write4(client, 0x108, 0x1006040f); - - /* - * VID_PLL Fraction (register 0x10c) = 0x2be2fe - * 28636360 * 0xf.15f17f0/4 = 108 MHz - * 432 MHz pre-postdivide - */ - - /* - * AUX_PLL Fraction = 0x1bb39ee - * 28636363 * 0x6.dd9cf70/0x10 = 32000 * 384 - * 196.6 MHz pre-postdivide - * FIXME < 200 MHz is out of specified valid range - * FIXME 28636363 ref_freq doesn't match VID PLL ref - */ - cx25840_write4(client, 0x110, 0x01bb39ee); - - /* - * SA_MCLK_SEL = 1 - * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider - */ - cx25840_write(client, 0x127, 0x50); - - if (is_cx2583x(state)) - break; - - /* src3/4/6_ctl */ - /* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */ - cx25840_write4(client, 0x900, 0x0801f77f); - cx25840_write4(client, 0x904, 0x0801f77f); - cx25840_write4(client, 0x90c, 0x0801f77f); - break; - - case 44100: - /* - * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 - * AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x10 - */ - cx25840_write4(client, 0x108, 0x1009040f); - - /* - * VID_PLL Fraction (register 0x10c) = 0x2be2fe - * 28636360 * 0xf.15f17f0/4 = 108 MHz - * 432 MHz pre-postdivide - */ - - /* - * AUX_PLL Fraction = 0x0ec6bd6 - * 28636363 * 0x9.7635eb0/0x10 = 44100 * 384 - * 271 MHz pre-postdivide - * FIXME 28636363 ref_freq doesn't match VID PLL ref - */ - cx25840_write4(client, 0x110, 0x00ec6bd6); - - /* - * SA_MCLK_SEL = 1 - * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider - */ - cx25840_write(client, 0x127, 0x50); - - if (is_cx2583x(state)) - break; - - /* src3/4/6_ctl */ - /* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */ - cx25840_write4(client, 0x900, 0x08016d59); - cx25840_write4(client, 0x904, 0x08016d59); - cx25840_write4(client, 0x90c, 0x08016d59); - break; - - case 48000: - /* - * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 - * AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x10 - */ - cx25840_write4(client, 0x108, 0x100a040f); - - /* - * VID_PLL Fraction (register 0x10c) = 0x2be2fe - * 28636360 * 0xf.15f17f0/4 = 108 MHz - * 432 MHz pre-postdivide - */ - - /* - * AUX_PLL Fraction = 0x098d6e5 - * 28636363 * 0xa.4c6b728/0x10 = 48000 * 384 - * 295 MHz pre-postdivide - * FIXME 28636363 ref_freq doesn't match VID PLL ref - */ - cx25840_write4(client, 0x110, 0x0098d6e5); - - /* - * SA_MCLK_SEL = 1 - * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider - */ - cx25840_write(client, 0x127, 0x50); - - if (is_cx2583x(state)) - break; - - /* src3/4/6_ctl */ - /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ - cx25840_write4(client, 0x900, 0x08014faa); - cx25840_write4(client, 0x904, 0x08014faa); - cx25840_write4(client, 0x90c, 0x08014faa); - break; - } - } else { - switch (freq) { - case 32000: - /* - * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 - * AUX_PLL Integer = 0x08, AUX PLL Post Divider = 0x1e - */ - cx25840_write4(client, 0x108, 0x1e08040f); - - /* - * VID_PLL Fraction (register 0x10c) = 0x2be2fe - * 28636360 * 0xf.15f17f0/4 = 108 MHz - * 432 MHz pre-postdivide - */ - - /* - * AUX_PLL Fraction = 0x12a0869 - * 28636363 * 0x8.9504348/0x1e = 32000 * 256 - * 246 MHz pre-postdivide - * FIXME 28636363 ref_freq doesn't match VID PLL ref - */ - cx25840_write4(client, 0x110, 0x012a0869); - - /* - * SA_MCLK_SEL = 1 - * SA_MCLK_DIV = 0x14 = 256/384 * AUX_PLL post dvivider - */ - cx25840_write(client, 0x127, 0x54); - - if (is_cx2583x(state)) - break; - - /* src1_ctl */ - /* 0x1.0000 = 32000/32000 */ - cx25840_write4(client, 0x8f8, 0x08010000); - - /* src3/4/6_ctl */ - /* 0x2.0000 = 2 * (32000/32000) */ - cx25840_write4(client, 0x900, 0x08020000); - cx25840_write4(client, 0x904, 0x08020000); - cx25840_write4(client, 0x90c, 0x08020000); - break; - - case 44100: - /* - * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 - * AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x18 - */ - cx25840_write4(client, 0x108, 0x1809040f); - - /* - * VID_PLL Fraction (register 0x10c) = 0x2be2fe - * 28636360 * 0xf.15f17f0/4 = 108 MHz - * 432 MHz pre-postdivide - */ - - /* - * AUX_PLL Fraction = 0x0ec6bd6 - * 28636363 * 0x9.7635eb0/0x18 = 44100 * 256 - * 271 MHz pre-postdivide - * FIXME 28636363 ref_freq doesn't match VID PLL ref - */ - cx25840_write4(client, 0x110, 0x00ec6bd6); - - /* - * SA_MCLK_SEL = 1 - * SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider - */ - cx25840_write(client, 0x127, 0x50); - - if (is_cx2583x(state)) - break; - - /* src1_ctl */ - /* 0x1.60cd = 44100/32000 */ - cx25840_write4(client, 0x8f8, 0x080160cd); - - /* src3/4/6_ctl */ - /* 0x1.7385 = 2 * (32000/44100) */ - cx25840_write4(client, 0x900, 0x08017385); - cx25840_write4(client, 0x904, 0x08017385); - cx25840_write4(client, 0x90c, 0x08017385); - break; - - case 48000: - /* - * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 - * AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x18 - */ - cx25840_write4(client, 0x108, 0x180a040f); - - /* - * VID_PLL Fraction (register 0x10c) = 0x2be2fe - * 28636360 * 0xf.15f17f0/4 = 108 MHz - * 432 MHz pre-postdivide - */ - - /* - * AUX_PLL Fraction = 0x098d6e5 - * 28636363 * 0xa.4c6b728/0x18 = 48000 * 256 - * 295 MHz pre-postdivide - * FIXME 28636363 ref_freq doesn't match VID PLL ref - */ - cx25840_write4(client, 0x110, 0x0098d6e5); - - /* - * SA_MCLK_SEL = 1 - * SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider - */ - cx25840_write(client, 0x127, 0x50); - - if (is_cx2583x(state)) - break; - - /* src1_ctl */ - /* 0x1.8000 = 48000/32000 */ - cx25840_write4(client, 0x8f8, 0x08018000); - - /* src3/4/6_ctl */ - /* 0x1.5555 = 2 * (32000/48000) */ - cx25840_write4(client, 0x900, 0x08015555); - cx25840_write4(client, 0x904, 0x08015555); - cx25840_write4(client, 0x90c, 0x08015555); - break; - } - } - - state->audclk_freq = freq; - - return 0; -} - -static inline int cx25836_set_audclk_freq(struct i2c_client *client, u32 freq) -{ - return cx25840_set_audclk_freq(client, freq); -} - -static int cx23885_set_audclk_freq(struct i2c_client *client, u32 freq) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - - if (state->aud_input != CX25840_AUDIO_SERIAL) { - switch (freq) { - case 32000: - case 44100: - case 48000: - /* We don't have register values - * so avoid destroying registers. */ - /* FIXME return -EINVAL; */ - break; - } - } else { - switch (freq) { - case 32000: - case 44100: - /* We don't have register values - * so avoid destroying registers. */ - /* FIXME return -EINVAL; */ - break; - - case 48000: - /* src1_ctl */ - /* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */ - cx25840_write4(client, 0x8f8, 0x0801867c); - - /* src3/4/6_ctl */ - /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ - cx25840_write4(client, 0x900, 0x08014faa); - cx25840_write4(client, 0x904, 0x08014faa); - cx25840_write4(client, 0x90c, 0x08014faa); - break; - } - } - - state->audclk_freq = freq; - - return 0; -} - -static int cx231xx_set_audclk_freq(struct i2c_client *client, u32 freq) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - - if (state->aud_input != CX25840_AUDIO_SERIAL) { - switch (freq) { - case 32000: - /* src3/4/6_ctl */ - /* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */ - cx25840_write4(client, 0x900, 0x0801f77f); - cx25840_write4(client, 0x904, 0x0801f77f); - cx25840_write4(client, 0x90c, 0x0801f77f); - break; - - case 44100: - /* src3/4/6_ctl */ - /* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */ - cx25840_write4(client, 0x900, 0x08016d59); - cx25840_write4(client, 0x904, 0x08016d59); - cx25840_write4(client, 0x90c, 0x08016d59); - break; - - case 48000: - /* src3/4/6_ctl */ - /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ - cx25840_write4(client, 0x900, 0x08014faa); - cx25840_write4(client, 0x904, 0x08014faa); - cx25840_write4(client, 0x90c, 0x08014faa); - break; - } - } else { - switch (freq) { - /* FIXME These cases make different assumptions about audclk */ - case 32000: - /* src1_ctl */ - /* 0x1.0000 = 32000/32000 */ - cx25840_write4(client, 0x8f8, 0x08010000); - - /* src3/4/6_ctl */ - /* 0x2.0000 = 2 * (32000/32000) */ - cx25840_write4(client, 0x900, 0x08020000); - cx25840_write4(client, 0x904, 0x08020000); - cx25840_write4(client, 0x90c, 0x08020000); - break; - - case 44100: - /* src1_ctl */ - /* 0x1.60cd = 44100/32000 */ - cx25840_write4(client, 0x8f8, 0x080160cd); - - /* src3/4/6_ctl */ - /* 0x1.7385 = 2 * (32000/44100) */ - cx25840_write4(client, 0x900, 0x08017385); - cx25840_write4(client, 0x904, 0x08017385); - cx25840_write4(client, 0x90c, 0x08017385); - break; - - case 48000: - /* src1_ctl */ - /* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */ - cx25840_write4(client, 0x8f8, 0x0801867c); - - /* src3/4/6_ctl */ - /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ - cx25840_write4(client, 0x900, 0x08014faa); - cx25840_write4(client, 0x904, 0x08014faa); - cx25840_write4(client, 0x90c, 0x08014faa); - break; - } - } - - state->audclk_freq = freq; - - return 0; -} - -static int set_audclk_freq(struct i2c_client *client, u32 freq) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - - if (freq != 32000 && freq != 44100 && freq != 48000) - return -EINVAL; - - if (is_cx231xx(state)) - return cx231xx_set_audclk_freq(client, freq); - - if (is_cx2388x(state)) - return cx23885_set_audclk_freq(client, freq); - - if (is_cx2583x(state)) - return cx25836_set_audclk_freq(client, freq); - - return cx25840_set_audclk_freq(client, freq); -} - -void cx25840_audio_set_path(struct i2c_client *client) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - - if (!is_cx2583x(state)) { - /* assert soft reset */ - cx25840_and_or(client, 0x810, ~0x1, 0x01); - - /* stop microcontroller */ - cx25840_and_or(client, 0x803, ~0x10, 0); - - /* Mute everything to prevent the PFFT! */ - cx25840_write(client, 0x8d3, 0x1f); - - if (state->aud_input == CX25840_AUDIO_SERIAL) { - /* Set Path1 to Serial Audio Input */ - cx25840_write4(client, 0x8d0, 0x01011012); - - /* The microcontroller should not be started for the - * non-tuner inputs: autodetection is specific for - * TV audio. */ - } else { - /* Set Path1 to Analog Demod Main Channel */ - cx25840_write4(client, 0x8d0, 0x1f063870); - } - } - - set_audclk_freq(client, state->audclk_freq); - - if (!is_cx2583x(state)) { - if (state->aud_input != CX25840_AUDIO_SERIAL) { - /* When the microcontroller detects the - * audio format, it will unmute the lines */ - cx25840_and_or(client, 0x803, ~0x10, 0x10); - } - - /* deassert soft reset */ - cx25840_and_or(client, 0x810, ~0x1, 0x00); - - /* Ensure the controller is running when we exit */ - if (is_cx2388x(state) || is_cx231xx(state)) - cx25840_and_or(client, 0x803, ~0x10, 0x10); - } -} - -static void set_volume(struct i2c_client *client, int volume) -{ - int vol; - - /* Convert the volume to msp3400 values (0-127) */ - vol = volume >> 9; - - /* now scale it up to cx25840 values - * -114dB to -96dB maps to 0 - * this should be 19, but in my testing that was 4dB too loud */ - if (vol <= 23) { - vol = 0; - } else { - vol -= 23; - } - - /* PATH1_VOLUME */ - cx25840_write(client, 0x8d4, 228 - (vol * 2)); -} - -static void set_balance(struct i2c_client *client, int balance) -{ - int bal = balance >> 8; - if (bal > 0x80) { - /* PATH1_BAL_LEFT */ - cx25840_and_or(client, 0x8d5, 0x7f, 0x80); - /* PATH1_BAL_LEVEL */ - cx25840_and_or(client, 0x8d5, ~0x7f, bal & 0x7f); - } else { - /* PATH1_BAL_LEFT */ - cx25840_and_or(client, 0x8d5, 0x7f, 0x00); - /* PATH1_BAL_LEVEL */ - cx25840_and_or(client, 0x8d5, ~0x7f, 0x80 - bal); - } -} - -int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct cx25840_state *state = to_state(sd); - int retval; - - if (!is_cx2583x(state)) - cx25840_and_or(client, 0x810, ~0x1, 1); - if (state->aud_input != CX25840_AUDIO_SERIAL) { - cx25840_and_or(client, 0x803, ~0x10, 0); - cx25840_write(client, 0x8d3, 0x1f); - } - retval = set_audclk_freq(client, freq); - if (state->aud_input != CX25840_AUDIO_SERIAL) - cx25840_and_or(client, 0x803, ~0x10, 0x10); - if (!is_cx2583x(state)) - cx25840_and_or(client, 0x810, ~0x1, 0); - return retval; -} - -static int cx25840_audio_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - if (state->mute->val) - set_volume(client, 0); - else - set_volume(client, state->volume->val); - break; - case V4L2_CID_AUDIO_BASS: - /* PATH1_EQ_BASS_VOL */ - cx25840_and_or(client, 0x8d9, ~0x3f, - 48 - (ctrl->val * 48 / 0xffff)); - break; - case V4L2_CID_AUDIO_TREBLE: - /* PATH1_EQ_TREBLE_VOL */ - cx25840_and_or(client, 0x8db, ~0x3f, - 48 - (ctrl->val * 48 / 0xffff)); - break; - case V4L2_CID_AUDIO_BALANCE: - set_balance(client, ctrl->val); - break; - default: - return -EINVAL; - } - return 0; -} - -const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops = { - .s_ctrl = cx25840_audio_s_ctrl, -}; diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c deleted file mode 100644 index d8eac3e30a7..00000000000 --- a/drivers/media/video/cx25840/cx25840-core.c +++ /dev/null @@ -1,5340 +0,0 @@ -/* cx25840 - Conexant CX25840 audio/video decoder driver - * - * Copyright (C) 2004 Ulf Eklund - * - * Based on the saa7115 driver and on the first version of Chris Kennedy's - * cx25840 driver. - * - * Changes by Tyler Trafford - * - cleanup/rewrite for V4L2 API (2005) - * - * VBI support by Hans Verkuil . - * - * NTSC sliced VBI support by Christopher Neufeld - * with additional fixes by Hans Verkuil . - * - * CX23885 support by Steven Toth . - * - * CX2388[578] IRQ handling, IO Pin mux configuration and other small fixes are - * Copyright (C) 2010 Andy Walls - * - * CX23888 DIF support for the HVR1850 - * Copyright (C) 2011 Steven Toth - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cx25840-core.h" - -MODULE_DESCRIPTION("Conexant CX25840 audio/video decoder driver"); -MODULE_AUTHOR("Ulf Eklund, Chris Kennedy, Hans Verkuil, Tyler Trafford"); -MODULE_LICENSE("GPL"); - -#define CX25840_VID_INT_STAT_REG 0x410 -#define CX25840_VID_INT_STAT_BITS 0x0000ffff -#define CX25840_VID_INT_MASK_BITS 0xffff0000 -#define CX25840_VID_INT_MASK_SHFT 16 -#define CX25840_VID_INT_MASK_REG 0x412 - -#define CX23885_AUD_MC_INT_MASK_REG 0x80c -#define CX23885_AUD_MC_INT_STAT_BITS 0xffff0000 -#define CX23885_AUD_MC_INT_CTRL_BITS 0x0000ffff -#define CX23885_AUD_MC_INT_STAT_SHFT 16 - -#define CX25840_AUD_INT_CTRL_REG 0x812 -#define CX25840_AUD_INT_STAT_REG 0x813 - -#define CX23885_PIN_CTRL_IRQ_REG 0x123 -#define CX23885_PIN_CTRL_IRQ_IR_STAT 0x40 -#define CX23885_PIN_CTRL_IRQ_AUD_STAT 0x20 -#define CX23885_PIN_CTRL_IRQ_VID_STAT 0x10 - -#define CX25840_IR_STATS_REG 0x210 -#define CX25840_IR_IRQEN_REG 0x214 - -static int cx25840_debug; - -module_param_named(debug,cx25840_debug, int, 0644); - -MODULE_PARM_DESC(debug, "Debugging messages [0=Off (default) 1=On]"); - - -/* ----------------------------------------------------------------------- */ -static void cx23888_std_setup(struct i2c_client *client); - -int cx25840_write(struct i2c_client *client, u16 addr, u8 value) -{ - u8 buffer[3]; - buffer[0] = addr >> 8; - buffer[1] = addr & 0xff; - buffer[2] = value; - return i2c_master_send(client, buffer, 3); -} - -int cx25840_write4(struct i2c_client *client, u16 addr, u32 value) -{ - u8 buffer[6]; - buffer[0] = addr >> 8; - buffer[1] = addr & 0xff; - buffer[2] = value & 0xff; - buffer[3] = (value >> 8) & 0xff; - buffer[4] = (value >> 16) & 0xff; - buffer[5] = value >> 24; - return i2c_master_send(client, buffer, 6); -} - -u8 cx25840_read(struct i2c_client * client, u16 addr) -{ - struct i2c_msg msgs[2]; - u8 tx_buf[2], rx_buf[1]; - - /* Write register address */ - tx_buf[0] = addr >> 8; - tx_buf[1] = addr & 0xff; - msgs[0].addr = client->addr; - msgs[0].flags = 0; - msgs[0].len = 2; - msgs[0].buf = (char *) tx_buf; - - /* Read data from register */ - msgs[1].addr = client->addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = 1; - msgs[1].buf = (char *) rx_buf; - - if (i2c_transfer(client->adapter, msgs, 2) < 2) - return 0; - - return rx_buf[0]; -} - -u32 cx25840_read4(struct i2c_client * client, u16 addr) -{ - struct i2c_msg msgs[2]; - u8 tx_buf[2], rx_buf[4]; - - /* Write register address */ - tx_buf[0] = addr >> 8; - tx_buf[1] = addr & 0xff; - msgs[0].addr = client->addr; - msgs[0].flags = 0; - msgs[0].len = 2; - msgs[0].buf = (char *) tx_buf; - - /* Read data from registers */ - msgs[1].addr = client->addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = 4; - msgs[1].buf = (char *) rx_buf; - - if (i2c_transfer(client->adapter, msgs, 2) < 2) - return 0; - - return (rx_buf[3] << 24) | (rx_buf[2] << 16) | (rx_buf[1] << 8) | - rx_buf[0]; -} - -int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned and_mask, - u8 or_value) -{ - return cx25840_write(client, addr, - (cx25840_read(client, addr) & and_mask) | - or_value); -} - -int cx25840_and_or4(struct i2c_client *client, u16 addr, u32 and_mask, - u32 or_value) -{ - return cx25840_write4(client, addr, - (cx25840_read4(client, addr) & and_mask) | - or_value); -} - -/* ----------------------------------------------------------------------- */ - -static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input, - enum cx25840_audio_input aud_input); - -/* ----------------------------------------------------------------------- */ - -static int cx23885_s_io_pin_config(struct v4l2_subdev *sd, size_t n, - struct v4l2_subdev_io_pin_config *p) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int i; - u32 pin_ctrl; - u8 gpio_oe, gpio_data, strength; - - pin_ctrl = cx25840_read4(client, 0x120); - gpio_oe = cx25840_read(client, 0x160); - gpio_data = cx25840_read(client, 0x164); - - for (i = 0; i < n; i++) { - strength = p[i].strength; - if (strength > CX25840_PIN_DRIVE_FAST) - strength = CX25840_PIN_DRIVE_FAST; - - switch (p[i].pin) { - case CX23885_PIN_IRQ_N_GPIO16: - if (p[i].function != CX23885_PAD_IRQ_N) { - /* GPIO16 */ - pin_ctrl &= ~(0x1 << 25); - } else { - /* IRQ_N */ - if (p[i].flags & - (V4L2_SUBDEV_IO_PIN_DISABLE | - V4L2_SUBDEV_IO_PIN_INPUT)) { - pin_ctrl &= ~(0x1 << 25); - } else { - pin_ctrl |= (0x1 << 25); - } - if (p[i].flags & - V4L2_SUBDEV_IO_PIN_ACTIVE_LOW) { - pin_ctrl &= ~(0x1 << 24); - } else { - pin_ctrl |= (0x1 << 24); - } - } - break; - case CX23885_PIN_IR_RX_GPIO19: - if (p[i].function != CX23885_PAD_GPIO19) { - /* IR_RX */ - gpio_oe |= (0x1 << 0); - pin_ctrl &= ~(0x3 << 18); - pin_ctrl |= (strength << 18); - } else { - /* GPIO19 */ - gpio_oe &= ~(0x1 << 0); - if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { - gpio_data &= ~(0x1 << 0); - gpio_data |= ((p[i].value & 0x1) << 0); - } - pin_ctrl &= ~(0x3 << 12); - pin_ctrl |= (strength << 12); - } - break; - case CX23885_PIN_IR_TX_GPIO20: - if (p[i].function != CX23885_PAD_GPIO20) { - /* IR_TX */ - gpio_oe |= (0x1 << 1); - if (p[i].flags & V4L2_SUBDEV_IO_PIN_DISABLE) - pin_ctrl &= ~(0x1 << 10); - else - pin_ctrl |= (0x1 << 10); - pin_ctrl &= ~(0x3 << 18); - pin_ctrl |= (strength << 18); - } else { - /* GPIO20 */ - gpio_oe &= ~(0x1 << 1); - if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { - gpio_data &= ~(0x1 << 1); - gpio_data |= ((p[i].value & 0x1) << 1); - } - pin_ctrl &= ~(0x3 << 12); - pin_ctrl |= (strength << 12); - } - break; - case CX23885_PIN_I2S_SDAT_GPIO21: - if (p[i].function != CX23885_PAD_GPIO21) { - /* I2S_SDAT */ - /* TODO: Input or Output config */ - gpio_oe |= (0x1 << 2); - pin_ctrl &= ~(0x3 << 22); - pin_ctrl |= (strength << 22); - } else { - /* GPIO21 */ - gpio_oe &= ~(0x1 << 2); - if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { - gpio_data &= ~(0x1 << 2); - gpio_data |= ((p[i].value & 0x1) << 2); - } - pin_ctrl &= ~(0x3 << 12); - pin_ctrl |= (strength << 12); - } - break; - case CX23885_PIN_I2S_WCLK_GPIO22: - if (p[i].function != CX23885_PAD_GPIO22) { - /* I2S_WCLK */ - /* TODO: Input or Output config */ - gpio_oe |= (0x1 << 3); - pin_ctrl &= ~(0x3 << 22); - pin_ctrl |= (strength << 22); - } else { - /* GPIO22 */ - gpio_oe &= ~(0x1 << 3); - if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { - gpio_data &= ~(0x1 << 3); - gpio_data |= ((p[i].value & 0x1) << 3); - } - pin_ctrl &= ~(0x3 << 12); - pin_ctrl |= (strength << 12); - } - break; - case CX23885_PIN_I2S_BCLK_GPIO23: - if (p[i].function != CX23885_PAD_GPIO23) { - /* I2S_BCLK */ - /* TODO: Input or Output config */ - gpio_oe |= (0x1 << 4); - pin_ctrl &= ~(0x3 << 22); - pin_ctrl |= (strength << 22); - } else { - /* GPIO23 */ - gpio_oe &= ~(0x1 << 4); - if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { - gpio_data &= ~(0x1 << 4); - gpio_data |= ((p[i].value & 0x1) << 4); - } - pin_ctrl &= ~(0x3 << 12); - pin_ctrl |= (strength << 12); - } - break; - } - } - - cx25840_write(client, 0x164, gpio_data); - cx25840_write(client, 0x160, gpio_oe); - cx25840_write4(client, 0x120, pin_ctrl); - return 0; -} - -static int common_s_io_pin_config(struct v4l2_subdev *sd, size_t n, - struct v4l2_subdev_io_pin_config *pincfg) -{ - struct cx25840_state *state = to_state(sd); - - if (is_cx2388x(state)) - return cx23885_s_io_pin_config(sd, n, pincfg); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static void init_dll1(struct i2c_client *client) -{ - /* This is the Hauppauge sequence used to - * initialize the Delay Lock Loop 1 (ADC DLL). */ - cx25840_write(client, 0x159, 0x23); - cx25840_write(client, 0x15a, 0x87); - cx25840_write(client, 0x15b, 0x06); - udelay(10); - cx25840_write(client, 0x159, 0xe1); - udelay(10); - cx25840_write(client, 0x15a, 0x86); - cx25840_write(client, 0x159, 0xe0); - cx25840_write(client, 0x159, 0xe1); - cx25840_write(client, 0x15b, 0x10); -} - -static void init_dll2(struct i2c_client *client) -{ - /* This is the Hauppauge sequence used to - * initialize the Delay Lock Loop 2 (ADC DLL). */ - cx25840_write(client, 0x15d, 0xe3); - cx25840_write(client, 0x15e, 0x86); - cx25840_write(client, 0x15f, 0x06); - udelay(10); - cx25840_write(client, 0x15d, 0xe1); - cx25840_write(client, 0x15d, 0xe0); - cx25840_write(client, 0x15d, 0xe1); -} - -static void cx25836_initialize(struct i2c_client *client) -{ - /* reset configuration is described on page 3-77 of the CX25836 datasheet */ - /* 2. */ - cx25840_and_or(client, 0x000, ~0x01, 0x01); - cx25840_and_or(client, 0x000, ~0x01, 0x00); - /* 3a. */ - cx25840_and_or(client, 0x15a, ~0x70, 0x00); - /* 3b. */ - cx25840_and_or(client, 0x15b, ~0x1e, 0x06); - /* 3c. */ - cx25840_and_or(client, 0x159, ~0x02, 0x02); - /* 3d. */ - udelay(10); - /* 3e. */ - cx25840_and_or(client, 0x159, ~0x02, 0x00); - /* 3f. */ - cx25840_and_or(client, 0x159, ~0xc0, 0xc0); - /* 3g. */ - cx25840_and_or(client, 0x159, ~0x01, 0x00); - cx25840_and_or(client, 0x159, ~0x01, 0x01); - /* 3h. */ - cx25840_and_or(client, 0x15b, ~0x1e, 0x10); -} - -static void cx25840_work_handler(struct work_struct *work) -{ - struct cx25840_state *state = container_of(work, struct cx25840_state, fw_work); - cx25840_loadfw(state->c); - wake_up(&state->fw_wait); -} - -static void cx25840_initialize(struct i2c_client *client) -{ - DEFINE_WAIT(wait); - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - struct workqueue_struct *q; - - /* datasheet startup in numbered steps, refer to page 3-77 */ - /* 2. */ - cx25840_and_or(client, 0x803, ~0x10, 0x00); - /* The default of this register should be 4, but I get 0 instead. - * Set this register to 4 manually. */ - cx25840_write(client, 0x000, 0x04); - /* 3. */ - init_dll1(client); - init_dll2(client); - cx25840_write(client, 0x136, 0x0a); - /* 4. */ - cx25840_write(client, 0x13c, 0x01); - cx25840_write(client, 0x13c, 0x00); - /* 5. */ - /* Do the firmware load in a work handler to prevent. - Otherwise the kernel is blocked waiting for the - bit-banging i2c interface to finish uploading the - firmware. */ - INIT_WORK(&state->fw_work, cx25840_work_handler); - init_waitqueue_head(&state->fw_wait); - q = create_singlethread_workqueue("cx25840_fw"); - prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE); - queue_work(q, &state->fw_work); - schedule(); - finish_wait(&state->fw_wait, &wait); - destroy_workqueue(q); - - /* 6. */ - cx25840_write(client, 0x115, 0x8c); - cx25840_write(client, 0x116, 0x07); - cx25840_write(client, 0x118, 0x02); - /* 7. */ - cx25840_write(client, 0x4a5, 0x80); - cx25840_write(client, 0x4a5, 0x00); - cx25840_write(client, 0x402, 0x00); - /* 8. */ - cx25840_and_or(client, 0x401, ~0x18, 0); - cx25840_and_or(client, 0x4a2, ~0x10, 0x10); - /* steps 8c and 8d are done in change_input() */ - /* 10. */ - cx25840_write(client, 0x8d3, 0x1f); - cx25840_write(client, 0x8e3, 0x03); - - cx25840_std_setup(client); - - /* trial and error says these are needed to get audio */ - cx25840_write(client, 0x914, 0xa0); - cx25840_write(client, 0x918, 0xa0); - cx25840_write(client, 0x919, 0x01); - - /* stereo preferred */ - cx25840_write(client, 0x809, 0x04); - /* AC97 shift */ - cx25840_write(client, 0x8cf, 0x0f); - - /* (re)set input */ - set_input(client, state->vid_input, state->aud_input); - - /* start microcontroller */ - cx25840_and_or(client, 0x803, ~0x10, 0x10); -} - -static void cx23885_initialize(struct i2c_client *client) -{ - DEFINE_WAIT(wait); - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - struct workqueue_struct *q; - - /* - * Come out of digital power down - * The CX23888, at least, needs this, otherwise registers aside from - * 0x0-0x2 can't be read or written. - */ - cx25840_write(client, 0x000, 0); - - /* Internal Reset */ - cx25840_and_or(client, 0x102, ~0x01, 0x01); - cx25840_and_or(client, 0x102, ~0x01, 0x00); - - /* Stop microcontroller */ - cx25840_and_or(client, 0x803, ~0x10, 0x00); - - /* DIF in reset? */ - cx25840_write(client, 0x398, 0); - - /* - * Trust the default xtal, no division - * '885: 28.636363... MHz - * '887: 25.000000 MHz - * '888: 50.000000 MHz - */ - cx25840_write(client, 0x2, 0x76); - - /* Power up all the PLL's and DLL */ - cx25840_write(client, 0x1, 0x40); - - /* Sys PLL */ - switch (state->id) { - case V4L2_IDENT_CX23888_AV: - /* - * 50.0 MHz * (0xb + 0xe8ba26/0x2000000)/4 = 5 * 28.636363 MHz - * 572.73 MHz before post divide - */ - /* HVR1850 or 50MHz xtal */ - cx25840_write(client, 0x2, 0x71); - cx25840_write4(client, 0x11c, 0x01d1744c); - cx25840_write4(client, 0x118, 0x00000416); - cx25840_write4(client, 0x404, 0x0010253e); - cx25840_write4(client, 0x42c, 0x42600000); - cx25840_write4(client, 0x44c, 0x161f1000); - break; - case V4L2_IDENT_CX23887_AV: - /* - * 25.0 MHz * (0x16 + 0x1d1744c/0x2000000)/4 = 5 * 28.636363 MHz - * 572.73 MHz before post divide - */ - cx25840_write4(client, 0x11c, 0x01d1744c); - cx25840_write4(client, 0x118, 0x00000416); - break; - case V4L2_IDENT_CX23885_AV: - default: - /* - * 28.636363 MHz * (0x14 + 0x0/0x2000000)/4 = 5 * 28.636363 MHz - * 572.73 MHz before post divide - */ - cx25840_write4(client, 0x11c, 0x00000000); - cx25840_write4(client, 0x118, 0x00000414); - break; - } - - /* Disable DIF bypass */ - cx25840_write4(client, 0x33c, 0x00000001); - - /* DIF Src phase inc */ - cx25840_write4(client, 0x340, 0x0df7df83); - - /* - * Vid PLL - * Setup for a BT.656 pixel clock of 13.5 Mpixels/second - * - * 28.636363 MHz * (0xf + 0x02be2c9/0x2000000)/4 = 8 * 13.5 MHz - * 432.0 MHz before post divide - */ - - /* HVR1850 */ - switch (state->id) { - case V4L2_IDENT_CX23888_AV: - /* 888/HVR1250 specific */ - cx25840_write4(client, 0x10c, 0x13333333); - cx25840_write4(client, 0x108, 0x00000515); - break; - default: - cx25840_write4(client, 0x10c, 0x002be2c9); - cx25840_write4(client, 0x108, 0x0000040f); - } - - /* Luma */ - cx25840_write4(client, 0x414, 0x00107d12); - - /* Chroma */ - cx25840_write4(client, 0x420, 0x3d008282); - - /* - * Aux PLL - * Initial setup for audio sample clock: - * 48 ksps, 16 bits/sample, x160 multiplier = 122.88 MHz - * Initial I2S output/master clock(?): - * 48 ksps, 16 bits/sample, x16 multiplier = 12.288 MHz - */ - switch (state->id) { - case V4L2_IDENT_CX23888_AV: - /* - * 50.0 MHz * (0x7 + 0x0bedfa4/0x2000000)/3 = 122.88 MHz - * 368.64 MHz before post divide - * 122.88 MHz / 0xa = 12.288 MHz - */ - /* HVR1850 or 50MHz xtal */ - cx25840_write4(client, 0x114, 0x017dbf48); - cx25840_write4(client, 0x110, 0x000a030e); - break; - case V4L2_IDENT_CX23887_AV: - /* - * 25.0 MHz * (0xe + 0x17dbf48/0x2000000)/3 = 122.88 MHz - * 368.64 MHz before post divide - * 122.88 MHz / 0xa = 12.288 MHz - */ - cx25840_write4(client, 0x114, 0x017dbf48); - cx25840_write4(client, 0x110, 0x000a030e); - break; - case V4L2_IDENT_CX23885_AV: - default: - /* - * 28.636363 MHz * (0xc + 0x1bf0c9e/0x2000000)/3 = 122.88 MHz - * 368.64 MHz before post divide - * 122.88 MHz / 0xa = 12.288 MHz - */ - cx25840_write4(client, 0x114, 0x01bf0c9e); - cx25840_write4(client, 0x110, 0x000a030c); - break; - }; - - /* ADC2 input select */ - cx25840_write(client, 0x102, 0x10); - - /* VIN1 & VIN5 */ - cx25840_write(client, 0x103, 0x11); - - /* Enable format auto detect */ - cx25840_write(client, 0x400, 0); - /* Fast subchroma lock */ - /* White crush, Chroma AGC & Chroma Killer enabled */ - cx25840_write(client, 0x401, 0xe8); - - /* Select AFE clock pad output source */ - cx25840_write(client, 0x144, 0x05); - - /* Drive GPIO2 direction and values for HVR1700 - * where an onboard mux selects the output of demodulator - * vs the 417. Failure to set this results in no DTV. - * It's safe to set this across all Hauppauge boards - * currently, regardless of the board type. - */ - cx25840_write(client, 0x160, 0x1d); - cx25840_write(client, 0x164, 0x00); - - /* Do the firmware load in a work handler to prevent. - Otherwise the kernel is blocked waiting for the - bit-banging i2c interface to finish uploading the - firmware. */ - INIT_WORK(&state->fw_work, cx25840_work_handler); - init_waitqueue_head(&state->fw_wait); - q = create_singlethread_workqueue("cx25840_fw"); - prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE); - queue_work(q, &state->fw_work); - schedule(); - finish_wait(&state->fw_wait, &wait); - destroy_workqueue(q); - - /* Call the cx23888 specific std setup func, we no longer rely on - * the generic cx24840 func. - */ - if (is_cx23888(state)) - cx23888_std_setup(client); - else - cx25840_std_setup(client); - - /* (re)set input */ - set_input(client, state->vid_input, state->aud_input); - - /* start microcontroller */ - cx25840_and_or(client, 0x803, ~0x10, 0x10); - - /* Disable and clear video interrupts - we don't use them */ - cx25840_write4(client, CX25840_VID_INT_STAT_REG, 0xffffffff); - - /* Disable and clear audio interrupts - we don't use them */ - cx25840_write(client, CX25840_AUD_INT_CTRL_REG, 0xff); - cx25840_write(client, CX25840_AUD_INT_STAT_REG, 0xff); - - /* CC raw enable */ - /* - VIP 1.1 control codes - 10bit, blue field enable. - * - enable raw data during vertical blanking. - * - enable ancillary Data insertion for 656 or VIP. - */ - cx25840_write4(client, 0x404, 0x0010253e); - - /* CC on - Undocumented Register */ - cx25840_write(client, 0x42f, 0x66); - - /* HVR-1250 / HVR1850 DIF related */ - /* Power everything up */ - cx25840_write4(client, 0x130, 0x0); - - /* Undocumented */ - cx25840_write4(client, 0x478, 0x6628021F); - - /* AFE_CLK_OUT_CTRL - Select the clock output source as output */ - cx25840_write4(client, 0x144, 0x5); - - /* I2C_OUT_CTL - I2S output configuration as - * Master, Sony, Left justified, left sample on WS=1 - */ - cx25840_write4(client, 0x918, 0x1a0); - - /* AFE_DIAG_CTRL1 */ - cx25840_write4(client, 0x134, 0x000a1800); - - /* AFE_DIAG_CTRL3 - Inverted Polarity for Audio and Video */ - cx25840_write4(client, 0x13c, 0x00310000); -} - -/* ----------------------------------------------------------------------- */ - -static void cx231xx_initialize(struct i2c_client *client) -{ - DEFINE_WAIT(wait); - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - struct workqueue_struct *q; - - /* Internal Reset */ - cx25840_and_or(client, 0x102, ~0x01, 0x01); - cx25840_and_or(client, 0x102, ~0x01, 0x00); - - /* Stop microcontroller */ - cx25840_and_or(client, 0x803, ~0x10, 0x00); - - /* DIF in reset? */ - cx25840_write(client, 0x398, 0); - - /* Trust the default xtal, no division */ - /* This changes for the cx23888 products */ - cx25840_write(client, 0x2, 0x76); - - /* Bring down the regulator for AUX clk */ - cx25840_write(client, 0x1, 0x40); - - /* Disable DIF bypass */ - cx25840_write4(client, 0x33c, 0x00000001); - - /* DIF Src phase inc */ - cx25840_write4(client, 0x340, 0x0df7df83); - - /* Luma */ - cx25840_write4(client, 0x414, 0x00107d12); - - /* Chroma */ - cx25840_write4(client, 0x420, 0x3d008282); - - /* ADC2 input select */ - cx25840_write(client, 0x102, 0x10); - - /* VIN1 & VIN5 */ - cx25840_write(client, 0x103, 0x11); - - /* Enable format auto detect */ - cx25840_write(client, 0x400, 0); - /* Fast subchroma lock */ - /* White crush, Chroma AGC & Chroma Killer enabled */ - cx25840_write(client, 0x401, 0xe8); - - /* Do the firmware load in a work handler to prevent. - Otherwise the kernel is blocked waiting for the - bit-banging i2c interface to finish uploading the - firmware. */ - INIT_WORK(&state->fw_work, cx25840_work_handler); - init_waitqueue_head(&state->fw_wait); - q = create_singlethread_workqueue("cx25840_fw"); - prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE); - queue_work(q, &state->fw_work); - schedule(); - finish_wait(&state->fw_wait, &wait); - destroy_workqueue(q); - - cx25840_std_setup(client); - - /* (re)set input */ - set_input(client, state->vid_input, state->aud_input); - - /* start microcontroller */ - cx25840_and_or(client, 0x803, ~0x10, 0x10); - - /* CC raw enable */ - cx25840_write(client, 0x404, 0x0b); - - /* CC on */ - cx25840_write(client, 0x42f, 0x66); - cx25840_write4(client, 0x474, 0x1e1e601a); -} - -/* ----------------------------------------------------------------------- */ - -void cx25840_std_setup(struct i2c_client *client) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - v4l2_std_id std = state->std; - int hblank, hactive, burst, vblank, vactive, sc; - int vblank656, src_decimation; - int luma_lpf, uv_lpf, comb; - u32 pll_int, pll_frac, pll_post; - - /* datasheet startup, step 8d */ - if (std & ~V4L2_STD_NTSC) - cx25840_write(client, 0x49f, 0x11); - else - cx25840_write(client, 0x49f, 0x14); - - if (std & V4L2_STD_625_50) { - hblank = 132; - hactive = 720; - burst = 93; - vblank = 36; - vactive = 580; - vblank656 = 40; - src_decimation = 0x21f; - luma_lpf = 2; - - if (std & V4L2_STD_SECAM) { - uv_lpf = 0; - comb = 0; - sc = 0x0a425f; - } else if (std == V4L2_STD_PAL_Nc) { - uv_lpf = 1; - comb = 0x20; - sc = 556453; - } else { - uv_lpf = 1; - comb = 0x20; - sc = 688739; - } - } else { - hactive = 720; - hblank = 122; - vactive = 487; - luma_lpf = 1; - uv_lpf = 1; - - src_decimation = 0x21f; - if (std == V4L2_STD_PAL_60) { - vblank = 26; - vblank656 = 26; - burst = 0x5b; - luma_lpf = 2; - comb = 0x20; - sc = 688739; - } else if (std == V4L2_STD_PAL_M) { - vblank = 20; - vblank656 = 24; - burst = 0x61; - comb = 0x20; - sc = 555452; - } else { - vblank = 26; - vblank656 = 26; - burst = 0x5b; - comb = 0x66; - sc = 556063; - } - } - - /* DEBUG: Displays configured PLL frequency */ - if (!is_cx231xx(state)) { - pll_int = cx25840_read(client, 0x108); - pll_frac = cx25840_read4(client, 0x10c) & 0x1ffffff; - pll_post = cx25840_read(client, 0x109); - v4l_dbg(1, cx25840_debug, client, - "PLL regs = int: %u, frac: %u, post: %u\n", - pll_int, pll_frac, pll_post); - - if (pll_post) { - int fin, fsc; - int pll = (28636363L * ((((u64)pll_int) << 25L) + pll_frac)) >> 25L; - - pll /= pll_post; - v4l_dbg(1, cx25840_debug, client, "PLL = %d.%06d MHz\n", - pll / 1000000, pll % 1000000); - v4l_dbg(1, cx25840_debug, client, "PLL/8 = %d.%06d MHz\n", - pll / 8000000, (pll / 8) % 1000000); - - fin = ((u64)src_decimation * pll) >> 12; - v4l_dbg(1, cx25840_debug, client, - "ADC Sampling freq = %d.%06d MHz\n", - fin / 1000000, fin % 1000000); - - fsc = (((u64)sc) * pll) >> 24L; - v4l_dbg(1, cx25840_debug, client, - "Chroma sub-carrier freq = %d.%06d MHz\n", - fsc / 1000000, fsc % 1000000); - - v4l_dbg(1, cx25840_debug, client, "hblank %i, hactive %i, " - "vblank %i, vactive %i, vblank656 %i, src_dec %i, " - "burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x, " - "sc 0x%06x\n", - hblank, hactive, vblank, vactive, vblank656, - src_decimation, burst, luma_lpf, uv_lpf, comb, sc); - } - } - - /* Sets horizontal blanking delay and active lines */ - cx25840_write(client, 0x470, hblank); - cx25840_write(client, 0x471, - 0xff & (((hblank >> 8) & 0x3) | (hactive << 4))); - cx25840_write(client, 0x472, hactive >> 4); - - /* Sets burst gate delay */ - cx25840_write(client, 0x473, burst); - - /* Sets vertical blanking delay and active duration */ - cx25840_write(client, 0x474, vblank); - cx25840_write(client, 0x475, - 0xff & (((vblank >> 8) & 0x3) | (vactive << 4))); - cx25840_write(client, 0x476, vactive >> 4); - cx25840_write(client, 0x477, vblank656); - - /* Sets src decimation rate */ - cx25840_write(client, 0x478, 0xff & src_decimation); - cx25840_write(client, 0x479, 0xff & (src_decimation >> 8)); - - /* Sets Luma and UV Low pass filters */ - cx25840_write(client, 0x47a, luma_lpf << 6 | ((uv_lpf << 4) & 0x30)); - - /* Enables comb filters */ - cx25840_write(client, 0x47b, comb); - - /* Sets SC Step*/ - cx25840_write(client, 0x47c, sc); - cx25840_write(client, 0x47d, 0xff & sc >> 8); - cx25840_write(client, 0x47e, 0xff & sc >> 16); - - /* Sets VBI parameters */ - if (std & V4L2_STD_625_50) { - cx25840_write(client, 0x47f, 0x01); - state->vbi_line_offset = 5; - } else { - cx25840_write(client, 0x47f, 0x00); - state->vbi_line_offset = 8; - } -} - -/* ----------------------------------------------------------------------- */ - -static void input_change(struct i2c_client *client) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - v4l2_std_id std = state->std; - - /* Follow step 8c and 8d of section 3.16 in the cx25840 datasheet */ - if (std & V4L2_STD_SECAM) { - cx25840_write(client, 0x402, 0); - } - else { - cx25840_write(client, 0x402, 0x04); - cx25840_write(client, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11); - } - cx25840_and_or(client, 0x401, ~0x60, 0); - cx25840_and_or(client, 0x401, ~0x60, 0x60); - - /* Don't write into audio registers on cx2583x chips */ - if (is_cx2583x(state)) - return; - - cx25840_and_or(client, 0x810, ~0x01, 1); - - if (state->radio) { - cx25840_write(client, 0x808, 0xf9); - cx25840_write(client, 0x80b, 0x00); - } - else if (std & V4L2_STD_525_60) { - /* Certain Hauppauge PVR150 models have a hardware bug - that causes audio to drop out. For these models the - audio standard must be set explicitly. - To be precise: it affects cards with tuner models - 85, 99 and 112 (model numbers from tveeprom). */ - int hw_fix = state->pvr150_workaround; - - if (std == V4L2_STD_NTSC_M_JP) { - /* Japan uses EIAJ audio standard */ - cx25840_write(client, 0x808, hw_fix ? 0x2f : 0xf7); - } else if (std == V4L2_STD_NTSC_M_KR) { - /* South Korea uses A2 audio standard */ - cx25840_write(client, 0x808, hw_fix ? 0x3f : 0xf8); - } else { - /* Others use the BTSC audio standard */ - cx25840_write(client, 0x808, hw_fix ? 0x1f : 0xf6); - } - cx25840_write(client, 0x80b, 0x00); - } else if (std & V4L2_STD_PAL) { - /* Autodetect audio standard and audio system */ - cx25840_write(client, 0x808, 0xff); - /* Since system PAL-L is pretty much non-existent and - not used by any public broadcast network, force - 6.5 MHz carrier to be interpreted as System DK, - this avoids DK audio detection instability */ - cx25840_write(client, 0x80b, 0x00); - } else if (std & V4L2_STD_SECAM) { - /* Autodetect audio standard and audio system */ - cx25840_write(client, 0x808, 0xff); - /* If only one of SECAM-DK / SECAM-L is required, then force - 6.5MHz carrier, else autodetect it */ - if ((std & V4L2_STD_SECAM_DK) && - !(std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) { - /* 6.5 MHz carrier to be interpreted as System DK */ - cx25840_write(client, 0x80b, 0x00); - } else if (!(std & V4L2_STD_SECAM_DK) && - (std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) { - /* 6.5 MHz carrier to be interpreted as System L */ - cx25840_write(client, 0x80b, 0x08); - } else { - /* 6.5 MHz carrier to be autodetected */ - cx25840_write(client, 0x80b, 0x10); - } - } - - cx25840_and_or(client, 0x810, ~0x01, 0); -} - -static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input, - enum cx25840_audio_input aud_input) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - u8 is_composite = (vid_input >= CX25840_COMPOSITE1 && - vid_input <= CX25840_COMPOSITE8); - u8 is_component = (vid_input & CX25840_COMPONENT_ON) == - CX25840_COMPONENT_ON; - u8 is_dif = (vid_input & CX25840_DIF_ON) == - CX25840_DIF_ON; - u8 is_svideo = (vid_input & CX25840_SVIDEO_ON) == - CX25840_SVIDEO_ON; - int luma = vid_input & 0xf0; - int chroma = vid_input & 0xf00; - u8 reg; - u32 val; - - v4l_dbg(1, cx25840_debug, client, - "decoder set video input %d, audio input %d\n", - vid_input, aud_input); - - if (vid_input >= CX25840_VIN1_CH1) { - v4l_dbg(1, cx25840_debug, client, "vid_input 0x%x\n", - vid_input); - reg = vid_input & 0xff; - is_composite = !is_component && - ((vid_input & CX25840_SVIDEO_ON) != CX25840_SVIDEO_ON); - - v4l_dbg(1, cx25840_debug, client, "mux cfg 0x%x comp=%d\n", - reg, is_composite); - } else if (is_composite) { - reg = 0xf0 + (vid_input - CX25840_COMPOSITE1); - } else { - if ((vid_input & ~0xff0) || - luma < CX25840_SVIDEO_LUMA1 || luma > CX25840_SVIDEO_LUMA8 || - chroma < CX25840_SVIDEO_CHROMA4 || chroma > CX25840_SVIDEO_CHROMA8) { - v4l_err(client, "0x%04x is not a valid video input!\n", - vid_input); - return -EINVAL; - } - reg = 0xf0 + ((luma - CX25840_SVIDEO_LUMA1) >> 4); - if (chroma >= CX25840_SVIDEO_CHROMA7) { - reg &= 0x3f; - reg |= (chroma - CX25840_SVIDEO_CHROMA7) >> 2; - } else { - reg &= 0xcf; - reg |= (chroma - CX25840_SVIDEO_CHROMA4) >> 4; - } - } - - /* The caller has previously prepared the correct routing - * configuration in reg (for the cx23885) so we have no - * need to attempt to flip bits for earlier av decoders. - */ - if (!is_cx2388x(state) && !is_cx231xx(state)) { - switch (aud_input) { - case CX25840_AUDIO_SERIAL: - /* do nothing, use serial audio input */ - break; - case CX25840_AUDIO4: reg &= ~0x30; break; - case CX25840_AUDIO5: reg &= ~0x30; reg |= 0x10; break; - case CX25840_AUDIO6: reg &= ~0x30; reg |= 0x20; break; - case CX25840_AUDIO7: reg &= ~0xc0; break; - case CX25840_AUDIO8: reg &= ~0xc0; reg |= 0x40; break; - - default: - v4l_err(client, "0x%04x is not a valid audio input!\n", - aud_input); - return -EINVAL; - } - } - - cx25840_write(client, 0x103, reg); - - /* Set INPUT_MODE to Composite, S-Video or Component */ - if (is_component) - cx25840_and_or(client, 0x401, ~0x6, 0x6); - else - cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02); - - if (is_cx2388x(state)) { - - /* Enable or disable the DIF for tuner use */ - if (is_dif) { - cx25840_and_or(client, 0x102, ~0x80, 0x80); - - /* Set of defaults for NTSC and PAL */ - cx25840_write4(client, 0x31c, 0xc2262600); - cx25840_write4(client, 0x320, 0xc2262600); - - /* 18271 IF - Nobody else yet uses a different - * tuner with the DIF, so these are reasonable - * assumptions (HVR1250 and HVR1850 specific). - */ - cx25840_write4(client, 0x318, 0xda262600); - cx25840_write4(client, 0x33c, 0x2a24c800); - cx25840_write4(client, 0x104, 0x0704dd00); - } else { - cx25840_write4(client, 0x300, 0x015c28f5); - - cx25840_and_or(client, 0x102, ~0x80, 0); - cx25840_write4(client, 0x340, 0xdf7df83); - cx25840_write4(client, 0x104, 0x0704dd80); - cx25840_write4(client, 0x314, 0x22400600); - cx25840_write4(client, 0x318, 0x40002600); - cx25840_write4(client, 0x324, 0x40002600); - cx25840_write4(client, 0x32c, 0x0250e620); - cx25840_write4(client, 0x39c, 0x01FF0B00); - - cx25840_write4(client, 0x410, 0xffff0dbf); - cx25840_write4(client, 0x414, 0x00137d03); - - /* on the 887, 0x418 is HSCALE_CTRL, on the 888 it is - CHROMA_CTRL */ - if (is_cx23888(state)) - cx25840_write4(client, 0x418, 0x01008080); - else - cx25840_write4(client, 0x418, 0x01000000); - - cx25840_write4(client, 0x41c, 0x00000000); - - /* on the 887, 0x420 is CHROMA_CTRL, on the 888 it is - CRUSH_CTRL */ - if (is_cx23888(state)) - cx25840_write4(client, 0x420, 0x001c3e0f); - else - cx25840_write4(client, 0x420, 0x001c8282); - - cx25840_write4(client, 0x42c, 0x42600000); - cx25840_write4(client, 0x430, 0x0000039b); - cx25840_write4(client, 0x438, 0x00000000); - - cx25840_write4(client, 0x440, 0xF8E3E824); - cx25840_write4(client, 0x444, 0x401040dc); - cx25840_write4(client, 0x448, 0xcd3f02a0); - cx25840_write4(client, 0x44c, 0x161f1000); - cx25840_write4(client, 0x450, 0x00000802); - - cx25840_write4(client, 0x91c, 0x01000000); - cx25840_write4(client, 0x8e0, 0x03063870); - cx25840_write4(client, 0x8d4, 0x7FFF0024); - cx25840_write4(client, 0x8d0, 0x00063073); - - cx25840_write4(client, 0x8c8, 0x00010000); - cx25840_write4(client, 0x8cc, 0x00080023); - - /* DIF BYPASS */ - cx25840_write4(client, 0x33c, 0x2a04c800); - } - - /* Reset the DIF */ - cx25840_write4(client, 0x398, 0); - } - - if (!is_cx2388x(state) && !is_cx231xx(state)) { - /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ - cx25840_and_or(client, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0); - /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2&CH3 */ - if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30) - cx25840_and_or(client, 0x102, ~0x4, 4); - else - cx25840_and_or(client, 0x102, ~0x4, 0); - } else { - /* Set DUAL_MODE_ADC2 to 1 if component*/ - cx25840_and_or(client, 0x102, ~0x4, is_component ? 0x4 : 0x0); - if (is_composite) { - /* ADC2 input select channel 2 */ - cx25840_and_or(client, 0x102, ~0x2, 0); - } else if (!is_component) { - /* S-Video */ - if (chroma >= CX25840_SVIDEO_CHROMA7) { - /* ADC2 input select channel 3 */ - cx25840_and_or(client, 0x102, ~0x2, 2); - } else { - /* ADC2 input select channel 2 */ - cx25840_and_or(client, 0x102, ~0x2, 0); - } - } - - /* cx23885 / SVIDEO */ - if (is_cx2388x(state) && is_svideo) { -#define AFE_CTRL (0x104) -#define MODE_CTRL (0x400) - cx25840_and_or(client, 0x102, ~0x2, 0x2); - - val = cx25840_read4(client, MODE_CTRL); - val &= 0xFFFFF9FF; - - /* YC */ - val |= 0x00000200; - val &= ~0x2000; - cx25840_write4(client, MODE_CTRL, val); - - val = cx25840_read4(client, AFE_CTRL); - - /* Chroma in select */ - val |= 0x00001000; - val &= 0xfffffe7f; - /* Clear VGA_SEL_CH2 and VGA_SEL_CH3 (bits 7 and 8). - * This sets them to use video rather than audio. - * Only one of the two will be in use. - */ - cx25840_write4(client, AFE_CTRL, val); - } else - cx25840_and_or(client, 0x102, ~0x2, 0); - } - - state->vid_input = vid_input; - state->aud_input = aud_input; - cx25840_audio_set_path(client); - input_change(client); - - if (is_cx2388x(state)) { - /* Audio channel 1 src : Parallel 1 */ - cx25840_write(client, 0x124, 0x03); - - /* Select AFE clock pad output source */ - cx25840_write(client, 0x144, 0x05); - - /* I2S_IN_CTL: I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 */ - cx25840_write(client, 0x914, 0xa0); - - /* I2S_OUT_CTL: - * I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 - * I2S_OUT_MASTER_MODE = Master - */ - cx25840_write(client, 0x918, 0xa0); - cx25840_write(client, 0x919, 0x01); - } else if (is_cx231xx(state)) { - /* Audio channel 1 src : Parallel 1 */ - cx25840_write(client, 0x124, 0x03); - - /* I2S_IN_CTL: I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 */ - cx25840_write(client, 0x914, 0xa0); - - /* I2S_OUT_CTL: - * I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 - * I2S_OUT_MASTER_MODE = Master - */ - cx25840_write(client, 0x918, 0xa0); - cx25840_write(client, 0x919, 0x01); - } - - if (is_cx2388x(state) && ((aud_input == CX25840_AUDIO7) || - (aud_input == CX25840_AUDIO6))) { - /* Configure audio from LR1 or LR2 input */ - cx25840_write4(client, 0x910, 0); - cx25840_write4(client, 0x8d0, 0x63073); - } else - if (is_cx2388x(state) && (aud_input == CX25840_AUDIO8)) { - /* Configure audio from tuner/sif input */ - cx25840_write4(client, 0x910, 0x12b000c9); - cx25840_write4(client, 0x8d0, 0x1f063870); - } - - if (is_cx23888(state)) { - /* HVR1850 */ - /* AUD_IO_CTRL - I2S Input, Parallel1*/ - /* - Channel 1 src - Parallel1 (Merlin out) */ - /* - Channel 2 src - Parallel2 (Merlin out) */ - /* - Channel 3 src - Parallel3 (Merlin AC97 out) */ - /* - I2S source and dir - Merlin, output */ - cx25840_write4(client, 0x124, 0x100); - - if (!is_dif) { - /* Stop microcontroller if we don't need it - * to avoid audio popping on svideo/composite use. - */ - cx25840_and_or(client, 0x803, ~0x10, 0x00); - } - } - - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int set_v4lstd(struct i2c_client *client) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - u8 fmt = 0; /* zero is autodetect */ - u8 pal_m = 0; - - /* First tests should be against specific std */ - if (state->std == V4L2_STD_NTSC_M_JP) { - fmt = 0x2; - } else if (state->std == V4L2_STD_NTSC_443) { - fmt = 0x3; - } else if (state->std == V4L2_STD_PAL_M) { - pal_m = 1; - fmt = 0x5; - } else if (state->std == V4L2_STD_PAL_N) { - fmt = 0x6; - } else if (state->std == V4L2_STD_PAL_Nc) { - fmt = 0x7; - } else if (state->std == V4L2_STD_PAL_60) { - fmt = 0x8; - } else { - /* Then, test against generic ones */ - if (state->std & V4L2_STD_NTSC) - fmt = 0x1; - else if (state->std & V4L2_STD_PAL) - fmt = 0x4; - else if (state->std & V4L2_STD_SECAM) - fmt = 0xc; - } - - v4l_dbg(1, cx25840_debug, client, "changing video std to fmt %i\n",fmt); - - /* Follow step 9 of section 3.16 in the cx25840 datasheet. - Without this PAL may display a vertical ghosting effect. - This happens for example with the Yuan MPC622. */ - if (fmt >= 4 && fmt < 8) { - /* Set format to NTSC-M */ - cx25840_and_or(client, 0x400, ~0xf, 1); - /* Turn off LCOMB */ - cx25840_and_or(client, 0x47b, ~6, 0); - } - cx25840_and_or(client, 0x400, ~0xf, fmt); - cx25840_and_or(client, 0x403, ~0x3, pal_m); - if (is_cx23888(state)) - cx23888_std_setup(client); - else - cx25840_std_setup(client); - if (!is_cx2583x(state)) - input_change(client); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int cx25840_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - cx25840_write(client, 0x414, ctrl->val - 128); - break; - - case V4L2_CID_CONTRAST: - cx25840_write(client, 0x415, ctrl->val << 1); - break; - - case V4L2_CID_SATURATION: - if (is_cx23888(state)) { - cx25840_write(client, 0x418, ctrl->val << 1); - cx25840_write(client, 0x419, ctrl->val << 1); - } else { - cx25840_write(client, 0x420, ctrl->val << 1); - cx25840_write(client, 0x421, ctrl->val << 1); - } - break; - - case V4L2_CID_HUE: - if (is_cx23888(state)) - cx25840_write(client, 0x41a, ctrl->val); - else - cx25840_write(client, 0x422, ctrl->val); - break; - - default: - return -EINVAL; - } - - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int cx25840_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - int HSC, VSC, Vsrc, Hsrc, filter, Vlines; - int is_50Hz = !(state->std & V4L2_STD_525_60); - - if (fmt->code != V4L2_MBUS_FMT_FIXED) - return -EINVAL; - - fmt->field = V4L2_FIELD_INTERLACED; - fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; - - if (is_cx23888(state)) { - Vsrc = (cx25840_read(client, 0x42a) & 0x3f) << 4; - Vsrc |= (cx25840_read(client, 0x429) & 0xf0) >> 4; - } else { - Vsrc = (cx25840_read(client, 0x476) & 0x3f) << 4; - Vsrc |= (cx25840_read(client, 0x475) & 0xf0) >> 4; - } - - if (is_cx23888(state)) { - Hsrc = (cx25840_read(client, 0x426) & 0x3f) << 4; - Hsrc |= (cx25840_read(client, 0x425) & 0xf0) >> 4; - } else { - Hsrc = (cx25840_read(client, 0x472) & 0x3f) << 4; - Hsrc |= (cx25840_read(client, 0x471) & 0xf0) >> 4; - } - - Vlines = fmt->height + (is_50Hz ? 4 : 7); - - if ((fmt->width * 16 < Hsrc) || (Hsrc < fmt->width) || - (Vlines * 8 < Vsrc) || (Vsrc < Vlines)) { - v4l_err(client, "%dx%d is not a valid size!\n", - fmt->width, fmt->height); - return -ERANGE; - } - - HSC = (Hsrc * (1 << 20)) / fmt->width - (1 << 20); - VSC = (1 << 16) - (Vsrc * (1 << 9) / Vlines - (1 << 9)); - VSC &= 0x1fff; - - if (fmt->width >= 385) - filter = 0; - else if (fmt->width > 192) - filter = 1; - else if (fmt->width > 96) - filter = 2; - else - filter = 3; - - v4l_dbg(1, cx25840_debug, client, "decoder set size %dx%d -> scale %ux%u\n", - fmt->width, fmt->height, HSC, VSC); - - /* HSCALE=HSC */ - cx25840_write(client, 0x418, HSC & 0xff); - cx25840_write(client, 0x419, (HSC >> 8) & 0xff); - cx25840_write(client, 0x41a, HSC >> 16); - /* VSCALE=VSC */ - cx25840_write(client, 0x41c, VSC & 0xff); - cx25840_write(client, 0x41d, VSC >> 8); - /* VS_INTRLACE=1 VFILT=filter */ - cx25840_write(client, 0x41e, 0x8 | filter); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static void log_video_status(struct i2c_client *client) -{ - static const char *const fmt_strs[] = { - "0x0", - "NTSC-M", "NTSC-J", "NTSC-4.43", - "PAL-BDGHI", "PAL-M", "PAL-N", "PAL-Nc", "PAL-60", - "0x9", "0xA", "0xB", - "SECAM", - "0xD", "0xE", "0xF" - }; - - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - u8 vidfmt_sel = cx25840_read(client, 0x400) & 0xf; - u8 gen_stat1 = cx25840_read(client, 0x40d); - u8 gen_stat2 = cx25840_read(client, 0x40e); - int vid_input = state->vid_input; - - v4l_info(client, "Video signal: %spresent\n", - (gen_stat2 & 0x20) ? "" : "not "); - v4l_info(client, "Detected format: %s\n", - fmt_strs[gen_stat1 & 0xf]); - - v4l_info(client, "Specified standard: %s\n", - vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection"); - - if (vid_input >= CX25840_COMPOSITE1 && - vid_input <= CX25840_COMPOSITE8) { - v4l_info(client, "Specified video input: Composite %d\n", - vid_input - CX25840_COMPOSITE1 + 1); - } else { - v4l_info(client, "Specified video input: S-Video (Luma In%d, Chroma In%d)\n", - (vid_input & 0xf0) >> 4, (vid_input & 0xf00) >> 8); - } - - v4l_info(client, "Specified audioclock freq: %d Hz\n", state->audclk_freq); -} - -/* ----------------------------------------------------------------------- */ - -static void log_audio_status(struct i2c_client *client) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - u8 download_ctl = cx25840_read(client, 0x803); - u8 mod_det_stat0 = cx25840_read(client, 0x804); - u8 mod_det_stat1 = cx25840_read(client, 0x805); - u8 audio_config = cx25840_read(client, 0x808); - u8 pref_mode = cx25840_read(client, 0x809); - u8 afc0 = cx25840_read(client, 0x80b); - u8 mute_ctl = cx25840_read(client, 0x8d3); - int aud_input = state->aud_input; - char *p; - - switch (mod_det_stat0) { - case 0x00: p = "mono"; break; - case 0x01: p = "stereo"; break; - case 0x02: p = "dual"; break; - case 0x04: p = "tri"; break; - case 0x10: p = "mono with SAP"; break; - case 0x11: p = "stereo with SAP"; break; - case 0x12: p = "dual with SAP"; break; - case 0x14: p = "tri with SAP"; break; - case 0xfe: p = "forced mode"; break; - default: p = "not defined"; - } - v4l_info(client, "Detected audio mode: %s\n", p); - - switch (mod_det_stat1) { - case 0x00: p = "not defined"; break; - case 0x01: p = "EIAJ"; break; - case 0x02: p = "A2-M"; break; - case 0x03: p = "A2-BG"; break; - case 0x04: p = "A2-DK1"; break; - case 0x05: p = "A2-DK2"; break; - case 0x06: p = "A2-DK3"; break; - case 0x07: p = "A1 (6.0 MHz FM Mono)"; break; - case 0x08: p = "AM-L"; break; - case 0x09: p = "NICAM-BG"; break; - case 0x0a: p = "NICAM-DK"; break; - case 0x0b: p = "NICAM-I"; break; - case 0x0c: p = "NICAM-L"; break; - case 0x0d: p = "BTSC/EIAJ/A2-M Mono (4.5 MHz FMMono)"; break; - case 0x0e: p = "IF FM Radio"; break; - case 0x0f: p = "BTSC"; break; - case 0x10: p = "high-deviation FM"; break; - case 0x11: p = "very high-deviation FM"; break; - case 0xfd: p = "unknown audio standard"; break; - case 0xfe: p = "forced audio standard"; break; - case 0xff: p = "no detected audio standard"; break; - default: p = "not defined"; - } - v4l_info(client, "Detected audio standard: %s\n", p); - v4l_info(client, "Audio microcontroller: %s\n", - (download_ctl & 0x10) ? - ((mute_ctl & 0x2) ? "detecting" : "running") : "stopped"); - - switch (audio_config >> 4) { - case 0x00: p = "undefined"; break; - case 0x01: p = "BTSC"; break; - case 0x02: p = "EIAJ"; break; - case 0x03: p = "A2-M"; break; - case 0x04: p = "A2-BG"; break; - case 0x05: p = "A2-DK1"; break; - case 0x06: p = "A2-DK2"; break; - case 0x07: p = "A2-DK3"; break; - case 0x08: p = "A1 (6.0 MHz FM Mono)"; break; - case 0x09: p = "AM-L"; break; - case 0x0a: p = "NICAM-BG"; break; - case 0x0b: p = "NICAM-DK"; break; - case 0x0c: p = "NICAM-I"; break; - case 0x0d: p = "NICAM-L"; break; - case 0x0e: p = "FM radio"; break; - case 0x0f: p = "automatic detection"; break; - default: p = "undefined"; - } - v4l_info(client, "Configured audio standard: %s\n", p); - - if ((audio_config >> 4) < 0xF) { - switch (audio_config & 0xF) { - case 0x00: p = "MONO1 (LANGUAGE A/Mono L+R channel for BTSC, EIAJ, A2)"; break; - case 0x01: p = "MONO2 (LANGUAGE B)"; break; - case 0x02: p = "MONO3 (STEREO forced MONO)"; break; - case 0x03: p = "MONO4 (NICAM ANALOG-Language C/Analog Fallback)"; break; - case 0x04: p = "STEREO"; break; - case 0x05: p = "DUAL1 (AB)"; break; - case 0x06: p = "DUAL2 (AC) (FM)"; break; - case 0x07: p = "DUAL3 (BC) (FM)"; break; - case 0x08: p = "DUAL4 (AC) (AM)"; break; - case 0x09: p = "DUAL5 (BC) (AM)"; break; - case 0x0a: p = "SAP"; break; - default: p = "undefined"; - } - v4l_info(client, "Configured audio mode: %s\n", p); - } else { - switch (audio_config & 0xF) { - case 0x00: p = "BG"; break; - case 0x01: p = "DK1"; break; - case 0x02: p = "DK2"; break; - case 0x03: p = "DK3"; break; - case 0x04: p = "I"; break; - case 0x05: p = "L"; break; - case 0x06: p = "BTSC"; break; - case 0x07: p = "EIAJ"; break; - case 0x08: p = "A2-M"; break; - case 0x09: p = "FM Radio"; break; - case 0x0f: p = "automatic standard and mode detection"; break; - default: p = "undefined"; - } - v4l_info(client, "Configured audio system: %s\n", p); - } - - if (aud_input) { - v4l_info(client, "Specified audio input: Tuner (In%d)\n", aud_input); - } else { - v4l_info(client, "Specified audio input: External\n"); - } - - switch (pref_mode & 0xf) { - case 0: p = "mono/language A"; break; - case 1: p = "language B"; break; - case 2: p = "language C"; break; - case 3: p = "analog fallback"; break; - case 4: p = "stereo"; break; - case 5: p = "language AC"; break; - case 6: p = "language BC"; break; - case 7: p = "language AB"; break; - default: p = "undefined"; - } - v4l_info(client, "Preferred audio mode: %s\n", p); - - if ((audio_config & 0xf) == 0xf) { - switch ((afc0 >> 3) & 0x3) { - case 0: p = "system DK"; break; - case 1: p = "system L"; break; - case 2: p = "autodetect"; break; - default: p = "undefined"; - } - v4l_info(client, "Selected 65 MHz format: %s\n", p); - - switch (afc0 & 0x7) { - case 0: p = "chroma"; break; - case 1: p = "BTSC"; break; - case 2: p = "EIAJ"; break; - case 3: p = "A2-M"; break; - case 4: p = "autodetect"; break; - default: p = "undefined"; - } - v4l_info(client, "Selected 45 MHz format: %s\n", p); - } -} - -/* ----------------------------------------------------------------------- */ - -/* This load_fw operation must be called to load the driver's firmware. - Without this the audio standard detection will fail and you will - only get mono. - - Since loading the firmware is often problematic when the driver is - compiled into the kernel I recommend postponing calling this function - until the first open of the video device. Another reason for - postponing it is that loading this firmware takes a long time (seconds) - due to the slow i2c bus speed. So it will speed up the boot process if - you can avoid loading the fw as long as the video device isn't used. */ -static int cx25840_load_fw(struct v4l2_subdev *sd) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!state->is_initialized) { - /* initialize and load firmware */ - state->is_initialized = 1; - if (is_cx2583x(state)) - cx25836_initialize(client); - else if (is_cx2388x(state)) - cx23885_initialize(client); - else if (is_cx231xx(state)) - cx231xx_initialize(client); - else - cx25840_initialize(client); - } - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int cx25840_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - reg->size = 1; - reg->val = cx25840_read(client, reg->reg & 0x0fff); - return 0; -} - -static int cx25840_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - cx25840_write(client, reg->reg & 0x0fff, reg->val & 0xff); - return 0; -} -#endif - -static int cx25840_s_audio_stream(struct v4l2_subdev *sd, int enable) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 v; - - if (is_cx2583x(state) || is_cx2388x(state) || is_cx231xx(state)) - return 0; - - v4l_dbg(1, cx25840_debug, client, "%s audio output\n", - enable ? "enable" : "disable"); - - if (enable) { - v = cx25840_read(client, 0x115) | 0x80; - cx25840_write(client, 0x115, v); - v = cx25840_read(client, 0x116) | 0x03; - cx25840_write(client, 0x116, v); - } else { - v = cx25840_read(client, 0x115) & ~(0x80); - cx25840_write(client, 0x115, v); - v = cx25840_read(client, 0x116) & ~(0x03); - cx25840_write(client, 0x116, v); - } - return 0; -} - -static int cx25840_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 v; - - v4l_dbg(1, cx25840_debug, client, "%s video output\n", - enable ? "enable" : "disable"); - if (enable) { - if (is_cx2388x(state) || is_cx231xx(state)) { - v = cx25840_read(client, 0x421) | 0x0b; - cx25840_write(client, 0x421, v); - } else { - v = cx25840_read(client, 0x115) | 0x0c; - cx25840_write(client, 0x115, v); - v = cx25840_read(client, 0x116) | 0x04; - cx25840_write(client, 0x116, v); - } - } else { - if (is_cx2388x(state) || is_cx231xx(state)) { - v = cx25840_read(client, 0x421) & ~(0x0b); - cx25840_write(client, 0x421, v); - } else { - v = cx25840_read(client, 0x115) & ~(0x0c); - cx25840_write(client, 0x115, v); - v = cx25840_read(client, 0x116) & ~(0x04); - cx25840_write(client, 0x116, v); - } - } - return 0; -} - -/* Query the current detected video format */ -static int cx25840_g_std(struct v4l2_subdev *sd, v4l2_std_id *std) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - v4l2_std_id stds[] = { - /* 0000 */ V4L2_STD_UNKNOWN, - - /* 0001 */ V4L2_STD_NTSC_M, - /* 0010 */ V4L2_STD_NTSC_M_JP, - /* 0011 */ V4L2_STD_NTSC_443, - /* 0100 */ V4L2_STD_PAL, - /* 0101 */ V4L2_STD_PAL_M, - /* 0110 */ V4L2_STD_PAL_N, - /* 0111 */ V4L2_STD_PAL_Nc, - /* 1000 */ V4L2_STD_PAL_60, - - /* 1001 */ V4L2_STD_UNKNOWN, - /* 1010 */ V4L2_STD_UNKNOWN, - /* 1001 */ V4L2_STD_UNKNOWN, - /* 1010 */ V4L2_STD_UNKNOWN, - /* 1011 */ V4L2_STD_UNKNOWN, - /* 1110 */ V4L2_STD_UNKNOWN, - /* 1111 */ V4L2_STD_UNKNOWN - }; - - u32 fmt = (cx25840_read4(client, 0x40c) >> 8) & 0xf; - *std = stds[ fmt ]; - - v4l_dbg(1, cx25840_debug, client, "g_std fmt = %x, v4l2_std_id = 0x%x\n", - fmt, (unsigned int)stds[ fmt ]); - - return 0; -} - -static int cx25840_g_input_status(struct v4l2_subdev *sd, u32 *status) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - /* A limited function that checks for signal status and returns - * the state. - */ - - /* Check for status of Horizontal lock (SRC lock isn't reliable) */ - if ((cx25840_read4(client, 0x40c) & 0x00010000) == 0) - *status |= V4L2_IN_ST_NO_SIGNAL; - - return 0; -} - -static int cx25840_s_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (state->radio == 0 && state->std == std) - return 0; - state->radio = 0; - state->std = std; - return set_v4lstd(client); -} - -static int cx25840_s_radio(struct v4l2_subdev *sd) -{ - struct cx25840_state *state = to_state(sd); - - state->radio = 1; - return 0; -} - -static int cx25840_s_video_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (is_cx23888(state)) - cx23888_std_setup(client); - - return set_input(client, input, state->aud_input); -} - -static int cx25840_s_audio_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (is_cx23888(state)) - cx23888_std_setup(client); - return set_input(client, state->vid_input, input); -} - -static int cx25840_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - input_change(client); - return 0; -} - -static int cx25840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 vpres = cx25840_read(client, 0x40e) & 0x20; - u8 mode; - int val = 0; - - if (state->radio) - return 0; - - vt->signal = vpres ? 0xffff : 0x0; - if (is_cx2583x(state)) - return 0; - - vt->capability |= - V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | - V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; - - mode = cx25840_read(client, 0x804); - - /* get rxsubchans and audmode */ - if ((mode & 0xf) == 1) - val |= V4L2_TUNER_SUB_STEREO; - else - val |= V4L2_TUNER_SUB_MONO; - - if (mode == 2 || mode == 4) - val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - - if (mode & 0x10) - val |= V4L2_TUNER_SUB_SAP; - - vt->rxsubchans = val; - vt->audmode = state->audmode; - return 0; -} - -static int cx25840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (state->radio || is_cx2583x(state)) - return 0; - - switch (vt->audmode) { - case V4L2_TUNER_MODE_MONO: - /* mono -> mono - stereo -> mono - bilingual -> lang1 */ - cx25840_and_or(client, 0x809, ~0xf, 0x00); - break; - case V4L2_TUNER_MODE_STEREO: - case V4L2_TUNER_MODE_LANG1: - /* mono -> mono - stereo -> stereo - bilingual -> lang1 */ - cx25840_and_or(client, 0x809, ~0xf, 0x04); - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - /* mono -> mono - stereo -> stereo - bilingual -> lang1/lang2 */ - cx25840_and_or(client, 0x809, ~0xf, 0x07); - break; - case V4L2_TUNER_MODE_LANG2: - /* mono -> mono - stereo -> stereo - bilingual -> lang2 */ - cx25840_and_or(client, 0x809, ~0xf, 0x01); - break; - default: - return -EINVAL; - } - state->audmode = vt->audmode; - return 0; -} - -static int cx25840_reset(struct v4l2_subdev *sd, u32 val) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (is_cx2583x(state)) - cx25836_initialize(client); - else if (is_cx2388x(state)) - cx23885_initialize(client); - else if (is_cx231xx(state)) - cx231xx_initialize(client); - else - cx25840_initialize(client); - return 0; -} - -static int cx25840_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, state->id, state->rev); -} - -static int cx25840_log_status(struct v4l2_subdev *sd) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - log_video_status(client); - if (!is_cx2583x(state)) - log_audio_status(client); - cx25840_ir_log_status(sd); - v4l2_ctrl_handler_log_status(&state->hdl, sd->name); - return 0; -} - -static int cx23885_irq_handler(struct v4l2_subdev *sd, u32 status, - bool *handled) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *c = v4l2_get_subdevdata(sd); - u8 irq_stat, aud_stat, aud_en, ir_stat, ir_en; - u32 vid_stat, aud_mc_stat; - bool block_handled; - int ret = 0; - - irq_stat = cx25840_read(c, CX23885_PIN_CTRL_IRQ_REG); - v4l_dbg(2, cx25840_debug, c, "AV Core IRQ status (entry): %s %s %s\n", - irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT ? "ir" : " ", - irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT ? "aud" : " ", - irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT ? "vid" : " "); - - if ((is_cx23885(state) || is_cx23887(state))) { - ir_stat = cx25840_read(c, CX25840_IR_STATS_REG); - ir_en = cx25840_read(c, CX25840_IR_IRQEN_REG); - v4l_dbg(2, cx25840_debug, c, - "AV Core ir IRQ status: %#04x disables: %#04x\n", - ir_stat, ir_en); - if (irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT) { - block_handled = false; - ret = cx25840_ir_irq_handler(sd, - status, &block_handled); - if (block_handled) - *handled = true; - } - } - - aud_stat = cx25840_read(c, CX25840_AUD_INT_STAT_REG); - aud_en = cx25840_read(c, CX25840_AUD_INT_CTRL_REG); - v4l_dbg(2, cx25840_debug, c, - "AV Core audio IRQ status: %#04x disables: %#04x\n", - aud_stat, aud_en); - aud_mc_stat = cx25840_read4(c, CX23885_AUD_MC_INT_MASK_REG); - v4l_dbg(2, cx25840_debug, c, - "AV Core audio MC IRQ status: %#06x enables: %#06x\n", - aud_mc_stat >> CX23885_AUD_MC_INT_STAT_SHFT, - aud_mc_stat & CX23885_AUD_MC_INT_CTRL_BITS); - if (irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT) { - if (aud_stat) { - cx25840_write(c, CX25840_AUD_INT_STAT_REG, aud_stat); - *handled = true; - } - } - - vid_stat = cx25840_read4(c, CX25840_VID_INT_STAT_REG); - v4l_dbg(2, cx25840_debug, c, - "AV Core video IRQ status: %#06x disables: %#06x\n", - vid_stat & CX25840_VID_INT_STAT_BITS, - vid_stat >> CX25840_VID_INT_MASK_SHFT); - if (irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT) { - if (vid_stat & CX25840_VID_INT_STAT_BITS) { - cx25840_write4(c, CX25840_VID_INT_STAT_REG, vid_stat); - *handled = true; - } - } - - irq_stat = cx25840_read(c, CX23885_PIN_CTRL_IRQ_REG); - v4l_dbg(2, cx25840_debug, c, "AV Core IRQ status (exit): %s %s %s\n", - irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT ? "ir" : " ", - irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT ? "aud" : " ", - irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT ? "vid" : " "); - - return ret; -} - -static int cx25840_irq_handler(struct v4l2_subdev *sd, u32 status, - bool *handled) -{ - struct cx25840_state *state = to_state(sd); - - *handled = false; - - /* Only support the CX2388[578] AV Core for now */ - if (is_cx2388x(state)) - return cx23885_irq_handler(sd, status, handled); - - return -ENODEV; -} - -/* ----------------------------------------------------------------------- */ - -#define DIF_PLL_FREQ_WORD (0x300) -#define DIF_BPF_COEFF01 (0x348) -#define DIF_BPF_COEFF23 (0x34c) -#define DIF_BPF_COEFF45 (0x350) -#define DIF_BPF_COEFF67 (0x354) -#define DIF_BPF_COEFF89 (0x358) -#define DIF_BPF_COEFF1011 (0x35c) -#define DIF_BPF_COEFF1213 (0x360) -#define DIF_BPF_COEFF1415 (0x364) -#define DIF_BPF_COEFF1617 (0x368) -#define DIF_BPF_COEFF1819 (0x36c) -#define DIF_BPF_COEFF2021 (0x370) -#define DIF_BPF_COEFF2223 (0x374) -#define DIF_BPF_COEFF2425 (0x378) -#define DIF_BPF_COEFF2627 (0x37c) -#define DIF_BPF_COEFF2829 (0x380) -#define DIF_BPF_COEFF3031 (0x384) -#define DIF_BPF_COEFF3233 (0x388) -#define DIF_BPF_COEFF3435 (0x38c) -#define DIF_BPF_COEFF36 (0x390) - -void cx23885_dif_setup(struct i2c_client *client, u32 ifHz) -{ - u64 pll_freq; - u32 pll_freq_word; - - v4l_dbg(1, cx25840_debug, client, "%s(%d)\n", __func__, ifHz); - - /* Assuming TV */ - /* Calculate the PLL frequency word based on the adjusted ifHz */ - pll_freq = div_u64((u64)ifHz * 268435456, 50000000); - pll_freq_word = (u32)pll_freq; - - cx25840_write4(client, DIF_PLL_FREQ_WORD, pll_freq_word); - - /* Round down to the nearest 100KHz */ - ifHz = (ifHz / 100000) * 100000; - - if (ifHz < 3000000) - ifHz = 3000000; - - if (ifHz > 16000000) - ifHz = 16000000; - - v4l_dbg(1, cx25840_debug, client, "%s(%d) again\n", __func__, ifHz); - - switch (ifHz) { - case 3000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00080012); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001e0024); - cx25840_write4(client, DIF_BPF_COEFF67, 0x001bfff8); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffb4ff50); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed8fe68); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe24fe34); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfebaffc7); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d031f); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x04f0065d); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x07010688); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x04c901d6); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe00f9d3); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600f342); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf235f337); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf64efb22); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0105070f); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x0c460fce); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 3100000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00070012); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00220032); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00370026); - cx25840_write4(client, DIF_BPF_COEFF89, 0xfff0ff91); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff0efe7c); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe01fdcc); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe0afedb); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x00440224); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0434060c); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0738074e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x06090361); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xff99fb39); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fef3b6); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf21af2a5); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf573fa33); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0034067d); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x0bfb0fb9); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 3200000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000000); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0004000e); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00200038); - cx25840_write4(client, DIF_BPF_COEFF67, 0x004c004f); - cx25840_write4(client, DIF_BPF_COEFF89, 0x002fffdf); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff5cfeb6); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe0dfd92); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd7ffe03); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36010a); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x03410575); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x072607d2); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x071804d5); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0134fcb7); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81ff451); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf223f22e); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf4a7f94b); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xff6405e8); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x0bae0fa4); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 3300000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00000008); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001a0036); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0056006d); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00670030); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xffbdff10); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe46fd8d); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd25fd4f); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35ffe0); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0224049f); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x06c9080e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x07ef0627); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x02c9fe45); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf961f513); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf250f1d2); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf3ecf869); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfe930552); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x0b5f0f8f); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 3400000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffd0001); - cx25840_write4(client, DIF_BPF_COEFF45, 0x000f002c); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0054007d); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0093007c); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0024ff82); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfea6fdbb); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd03fcca); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51feb9); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x00eb0392); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x06270802); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x08880750); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x044dffdb); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabdf5f8); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2a0f193); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf342f78f); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfdc404b9); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x0b0e0f78); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 3500000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffafff9); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0002001b); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0046007d); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00ad00ba); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00870000); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xff26fe1a); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd1bfc7e); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fda4); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xffa5025c); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x054507ad); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x08dd0847); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x05b80172); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2ef6ff); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf313f170); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf2abf6bd); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfcf6041f); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x0abc0f61); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 3600000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff3); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfff50006); - cx25840_write4(client, DIF_BPF_COEFF67, 0x002f006c); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00b200e3); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00dc007e); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xffb9fea0); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd6bfc71); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fcb1); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe65010b); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x042d0713); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x08ec0906); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x07020302); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaff823); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3a7f16a); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf228f5f5); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfc2a0384); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x0a670f4a); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 3700000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff7ffef); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe9fff1); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0010004d); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00a100f2); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x011a00f0); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x0053ff44); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdedfca2); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fbef); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd39ffae); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x02ea0638); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x08b50987); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x08230483); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xff39f960); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf45bf180); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf1b8f537); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfb6102e7); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x0a110f32); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 3800000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9ffee); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe1ffdd); - cx25840_write4(client, DIF_BPF_COEFF67, 0xfff00024); - cx25840_write4(client, DIF_BPF_COEFF89, 0x007c00e5); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x013a014a); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x00e6fff8); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe98fd0f); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fb67); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc32fe54); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x01880525); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x083909c7); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x091505ee); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c7fab3); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf52df1b4); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf15df484); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfa9b0249); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x09ba0f19); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 3900000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000000); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffbfff0); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffcf); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffd1fff6); - cx25840_write4(client, DIF_BPF_COEFF89, 0x004800be); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x01390184); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x016300ac); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xff5efdb1); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fb23); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb5cfd0d); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x001703e4); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x077b09c4); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x09d2073c); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0251fc18); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf61cf203); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf118f3dc); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf9d801aa); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x09600eff); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 4000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffefff4); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe1ffc8); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffbaffca); - cx25840_write4(client, DIF_BPF_COEFF89, 0x000b0082); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x01170198); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01c10152); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0030fe7b); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fb24); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfac3fbe9); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfea5027f); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x0683097f); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a560867); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d2fd89); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf723f26f); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0e8f341); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf919010a); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x09060ee5); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 4100000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0002fffb); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe8ffca); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffacffa4); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffcd0036); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00d70184); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01f601dc); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x00ffff60); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fb6d); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa6efaf5); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfd410103); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x055708f9); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a9e0969); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0543ff02); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf842f2f5); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0cef2b2); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf85e006b); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x08aa0ecb); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 4200000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00050003); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfff3ffd3); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffaaff8b); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff95ffe5); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0080014a); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01fe023f); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x01ba0050); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fbf8); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa62fa3b); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfbf9ff7e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x04010836); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0aa90a3d); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x069f007f); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf975f395); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0cbf231); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf7a9ffcb); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x084c0eaf); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 4300000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0008000a); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0000ffe4); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffb4ff81); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff6aff96); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x001c00f0); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01d70271); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0254013b); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fcbd); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa9ff9c5); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfadbfdfe); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x028c073b); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a750adf); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e101fa); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfab8f44e); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0ddf1be); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf6f9ff2b); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x07ed0e94); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 4400000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0009000f); - cx25840_write4(client, DIF_BPF_COEFF45, 0x000efff8); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9ff87); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff52ff54); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xffb5007e); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01860270); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02c00210); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fdb2); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb22f997); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9f2fc90); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x0102060f); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a050b4c); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0902036e); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfc0af51e); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf106f15a); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf64efe8b); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x078d0e77); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 4500000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00080012); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0019000e); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5ff9e); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff4fff25); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff560000); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x0112023b); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f702c0); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfec8); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfbe5f9b3); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf947fb41); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xff7004b9); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x095a0b81); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a0004d8); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfd65f603); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf144f104); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf5aafdec); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x072b0e5a); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 4600000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00060012); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00200022); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0005ffc1); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff61ff10); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff09ff82); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x008601d7); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f50340); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fff0); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfcddfa19); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8e2fa1e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfde30343); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x08790b7f); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad50631); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfec7f6fc); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf198f0bd); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf50dfd4e); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x06c90e3d); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 4700000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0003000f); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00220030); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0025ffed); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff87ff15); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed6ff10); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xffed014c); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02b90386); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x03110119); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfdfefac4); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8c6f92f); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfc6701b7); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x07670b44); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7e0776); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x002df807); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf200f086); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf477fcb1); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x06650e1e); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 4800000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0xffff0009); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001e0038); - cx25840_write4(client, DIF_BPF_COEFF67, 0x003f001b); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffbcff36); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec2feb6); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xff5600a5); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0248038d); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b00232); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xff39fbab); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8f4f87f); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb060020); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x062a0ad2); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf908a3); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0192f922); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf27df05e); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf3e8fc14); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x06000e00); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 4900000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffc0002); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00160037); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00510046); - cx25840_write4(client, DIF_BPF_COEFF89, 0xfff9ff6d); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed0fe7c); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfecefff0); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x01aa0356); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413032b); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x007ffcc5); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf96cf812); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf9cefe87); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x04c90a2c); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c4309b4); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x02f3fa4a); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf30ef046); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf361fb7a); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x059b0de0); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 5000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fffa); - cx25840_write4(client, DIF_BPF_COEFF45, 0x000a002d); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00570067); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0037ffb5); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfefffe68); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe62ff3d); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x00ec02e3); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x043503f6); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x01befe05); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa27f7ee); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf8c6fcf8); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x034c0954); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c5c0aa4); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x044cfb7e); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf3b1f03f); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf2e2fae1); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x05340dc0); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 5100000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff4); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfffd001e); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0051007b); - cx25840_write4(client, DIF_BPF_COEFF89, 0x006e0006); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff48fe7c); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe1bfe9a); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x001d023e); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x04130488); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x02e6ff5b); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfb1ef812); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7f7fb7f); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x01bc084e); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c430b72); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x059afcba); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf467f046); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf26cfa4a); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x04cd0da0); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 5200000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8ffef); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfff00009); - cx25840_write4(client, DIF_BPF_COEFF67, 0x003f007f); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00980056); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xffa5feb6); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe00fe15); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xff4b0170); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b004d7); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x03e800b9); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfc48f87f); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf768fa23); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0022071f); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf90c1b); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x06dafdfd); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf52df05e); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf1fef9b5); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x04640d7f); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 5300000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9ffee); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe6fff3); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00250072); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00af009c); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x000cff10); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe13fdb8); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe870089); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x031104e1); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x04b8020f); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfd98f92f); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf71df8f0); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe8805ce); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7e0c9c); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0808ff44); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf603f086); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf19af922); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x03fb0d5e); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 5400000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffcffef); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe0ffe0); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00050056); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00b000d1); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0071ff82); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe53fd8c); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfddfff99); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x024104a3); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x054a034d); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xff01fa1e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf717f7ed); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfcf50461); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad50cf4); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0921008d); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf6e7f0bd); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf13ff891); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x03920d3b); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 5500000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffffff3); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffd1); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5002f); - cx25840_write4(client, DIF_BPF_COEFF89, 0x009c00ed); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00cb0000); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfebafd94); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd61feb0); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d0422); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x05970464); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0074fb41); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf759f721); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfb7502de); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a000d21); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0a2201d4); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf7d9f104); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf0edf804); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x03280d19); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 5600000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0003fffa); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe3ffc9); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffc90002); - cx25840_write4(client, DIF_BPF_COEFF89, 0x007500ef); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x010e007e); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xff3dfdcf); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd16fddd); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x00440365); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x059b0548); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x01e3fc90); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7dff691); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfa0f014d); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x09020d23); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0b0a0318); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf8d7f15a); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf0a5f779); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x02bd0cf6); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 5700000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00060001); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffecffc9); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffb4ffd4); - cx25840_write4(client, DIF_BPF_COEFF89, 0x004000d5); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x013600f0); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xffd3fe39); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd04fd31); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xff360277); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x055605ef); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x033efdfe); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf8a5f642); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf8cbffb6); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e10cfb); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0bd50456); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf9dff1be); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf067f6f2); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x02520cd2); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 5800000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00080009); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfff8ffd2); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffaaffac); - cx25840_write4(client, DIF_BPF_COEFF89, 0x000200a3); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x013c014a); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x006dfec9); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd2bfcb7); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe350165); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x04cb0651); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0477ff7e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf9a5f635); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf7b1fe20); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x069f0ca8); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0c81058b); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xfaf0f231); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf033f66d); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x01e60cae); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 5900000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0009000e); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0005ffe1); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffacff90); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffc5005f); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x01210184); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x00fcff72); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd8afc77); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51003f); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x04020669); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x05830103); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfad7f66b); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf6c8fc93); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x05430c2b); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d0d06b5); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xfc08f2b2); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf00af5ec); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x017b0c89); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 6000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00070012); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0012fff5); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffbaff82); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff8e000f); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00e80198); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01750028); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe18fc75); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99ff15); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x03050636); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0656027f); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfc32f6e2); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf614fb17); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d20b87); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d7707d2); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xfd26f341); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xefeaf56f); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x010f0c64); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 6100000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00050012); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001c000b); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffd1ff84); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff66ffbe); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00960184); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01cd00da); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfeccfcb2); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fdf9); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x01e005bc); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x06e703e4); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfdabf798); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf599f9b3); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x02510abd); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0dbf08df); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xfe48f3dc); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xefd5f4f6); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x00a20c3e); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 6200000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0002000f); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0021001f); - cx25840_write4(client, DIF_BPF_COEFF67, 0xfff0ff97); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff50ff74); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0034014a); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01fa0179); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xff97fd2a); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fcfa); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x00a304fe); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x07310525); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xff37f886); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf55cf86e); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c709d0); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0de209db); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xff6df484); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xefcbf481); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x00360c18); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 6300000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffe000a); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0021002f); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0010ffb8); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff50ff3b); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xffcc00f0); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01fa01fa); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0069fdd4); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fc26); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xff5d0407); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x07310638); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x00c9f9a8); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf55cf74e); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xff3908c3); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0de20ac3); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0093f537); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xefcbf410); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xffca0bf2); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 6400000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffb0003); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001c0037); - cx25840_write4(client, DIF_BPF_COEFF67, 0x002fffe2); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff66ff17); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff6a007e); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01cd0251); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0134fea5); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fb8b); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe2002e0); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x06e70713); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x0255faf5); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf599f658); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaf0799); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0dbf0b96); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x01b8f5f5); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xefd5f3a3); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xff5e0bca); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 6500000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fffb); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00120037); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00460010); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff8eff0f); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff180000); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01750276); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x01e8ff8d); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fb31); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfcfb0198); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x065607ad); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x03cefc64); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf614f592); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2e0656); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d770c52); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x02daf6bd); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xefeaf33b); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xfef10ba3); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 6600000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff7fff5); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0005002f); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0054003c); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffc5ff22); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfedfff82); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x00fc0267); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0276007e); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fb1c); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfbfe003e); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x05830802); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x0529fdec); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf6c8f4fe); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabd04ff); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d0d0cf6); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x03f8f78f); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf00af2d7); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xfe850b7b); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 6700000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff0); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfff80020); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00560060); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0002ff4e); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec4ff10); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x006d0225); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02d50166); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fb4e); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb35fee1); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0477080e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x065bff82); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf7b1f4a0); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf9610397); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0c810d80); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0510f869); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf033f278); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xfe1a0b52); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 6800000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffaffee); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffec000c); - cx25840_write4(client, DIF_BPF_COEFF67, 0x004c0078); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0040ff8e); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfecafeb6); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xffd301b6); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02fc0235); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fbc5); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfaaafd90); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x033e07d2); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x075b011b); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf8cbf47a); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81f0224); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0bd50def); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0621f94b); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf067f21e); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xfdae0b29); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 6900000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffdffef); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe3fff6); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0037007f); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0075ffdc); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfef2fe7c); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xff3d0122); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02ea02dd); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fc79); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa65fc5d); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x01e3074e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x082102ad); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfa0ff48c); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fe00a9); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0b0a0e43); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0729fa33); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf0a5f1c9); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xfd430b00); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 7000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0001fff3); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffe2); - cx25840_write4(client, DIF_BPF_COEFF67, 0x001b0076); - cx25840_write4(client, DIF_BPF_COEFF89, 0x009c002d); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff35fe68); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfeba0076); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x029f0352); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfd60); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa69fb53); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x00740688); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x08a7042d); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfb75f4d6); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600ff2d); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0a220e7a); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0827fb22); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf0edf17a); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xfcd80ad6); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 7100000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0004fff9); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe0ffd2); - cx25840_write4(client, DIF_BPF_COEFF67, 0xfffb005e); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00b0007a); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff8ffe7c); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe53ffc1); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0221038c); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fe6e); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfab6fa80); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xff010587); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x08e90590); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfcf5f556); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf52bfdb3); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x09210e95); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0919fc15); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf13ff12f); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xfc6e0aab); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 7200000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00070000); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe6ffc9); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffdb0039); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00af00b8); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfff4feb6); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe13ff10); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x01790388); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0311ff92); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb48f9ed); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfd980453); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x08e306cd); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe88f60a); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf482fc40); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x08080e93); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x09fdfd0c); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf19af0ea); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xfc050a81); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 7300000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00080008); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfff0ffc9); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffc1000d); - cx25840_write4(client, DIF_BPF_COEFF89, 0x009800e2); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x005bff10); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe00fe74); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x00b50345); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b000bc); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc18f9a1); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfc4802f9); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x089807dc); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0022f6f0); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf407fada); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x06da0e74); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0ad3fe06); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf1fef0ab); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xfb9c0a55); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 7400000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0008000e); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfffdffd0); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffafffdf); - cx25840_write4(client, DIF_BPF_COEFF89, 0x006e00f2); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00b8ff82); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe1bfdf8); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xffe302c8); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x041301dc); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd1af99e); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfb1e0183); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x080908b5); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x01bcf801); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3bdf985); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x059a0e38); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0b99ff03); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf26cf071); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xfb330a2a); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 7500000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00070011); - cx25840_write4(client, DIF_BPF_COEFF45, 0x000affdf); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffa9ffb5); - cx25840_write4(client, DIF_BPF_COEFF89, 0x003700e6); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x01010000); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe62fda8); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xff140219); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x043502e1); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe42f9e6); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa270000); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x073a0953); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x034cf939); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3a4f845); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x044c0de1); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0c4f0000); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf2e2f03c); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xfacc09fe); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 7600000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00040012); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0016fff3); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffafff95); - cx25840_write4(client, DIF_BPF_COEFF89, 0xfff900c0); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0130007e); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfecefd89); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe560146); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x041303bc); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xff81fa76); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf96cfe7d); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x063209b1); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x04c9fa93); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3bdf71e); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x02f30d6e); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0cf200fd); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf361f00e); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xfa6509d1); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 7700000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00010010); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001e0008); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffc1ff84); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffbc0084); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x013e00f0); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xff56fd9f); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdb8005c); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b00460); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x00c7fb45); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8f4fd07); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x04fa09ce); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x062afc07); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf407f614); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x01920ce0); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0d8301fa); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf3e8efe5); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xfa0009a4); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 7800000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffd000b); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0022001d); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffdbff82); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff870039); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x012a014a); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xffedfde7); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd47ff6b); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x031104c6); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0202fc4c); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8c6fbad); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x039909a7); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0767fd8e); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf482f52b); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x002d0c39); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0e0002f4); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf477efc2); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf99b0977); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 7900000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffa0004); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0020002d); - cx25840_write4(client, DIF_BPF_COEFF67, 0xfffbff91); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff61ffe8); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00f70184); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x0086fe5c); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd0bfe85); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x024104e5); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0323fd7d); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8e2fa79); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x021d093f); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0879ff22); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf52bf465); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfec70b79); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0e6803eb); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf50defa5); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf937094a); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 8000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fffd); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00190036); - cx25840_write4(client, DIF_BPF_COEFF67, 0x001bffaf); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff4fff99); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00aa0198); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x0112fef3); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd09fdb9); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d04be); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x041bfecc); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf947f978); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x00900897); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x095a00b9); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600f3c5); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfd650aa3); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0ebc04de); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf5aaef8e); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf8d5091c); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 8100000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff7fff6); - cx25840_write4(client, DIF_BPF_COEFF45, 0x000e0038); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0037ffd7); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff52ff56); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x004b0184); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x0186ffa1); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd40fd16); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x00440452); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x04de0029); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9f2f8b2); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfefe07b5); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a05024d); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fef34d); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfc0a09b8); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0efa05cd); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf64eef7d); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf87308ed); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 8200000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff0); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00000031); - cx25840_write4(client, DIF_BPF_COEFF67, 0x004c0005); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff6aff27); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xffe4014a); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01d70057); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdacfca6); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xff3603a7); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x05610184); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfadbf82e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfd74069f); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a7503d6); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81ff2ff); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfab808b9); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0f2306b5); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf6f9ef72); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf81308bf); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 8300000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffbffee); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfff30022); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00560032); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff95ff10); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff8000f0); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01fe0106); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe46fc71); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe3502c7); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x059e02ce); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfbf9f7f2); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfbff055b); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0aa9054c); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf961f2db); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf97507aa); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0f350797); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf7a9ef6d); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf7b40890); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 8400000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffeffee); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe8000f); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00540058); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffcdff14); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff29007e); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01f6019e); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xff01fc7c); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd5101bf); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x059203f6); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfd41f7fe); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfaa903f3); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a9e06a9); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabdf2e2); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf842068b); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0f320871); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf85eef6e); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf7560860); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 8500000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0002fff2); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe1fff9); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00460073); - cx25840_write4(client, DIF_BPF_COEFF89, 0x000bff34); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfee90000); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01c10215); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xffd0fcc5); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99009d); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x053d04f1); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfea5f853); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf97d0270); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a5607e4); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2ef314); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf723055f); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0f180943); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf919ef75); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf6fa0830); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 8600000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0005fff8); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffe4); - cx25840_write4(client, DIF_BPF_COEFF67, 0x002f007f); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0048ff6b); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec7ff82); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x0163025f); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x00a2fd47); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17ff73); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x04a405b2); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0017f8ed); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf88500dc); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x09d208f9); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaff370); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf61c0429); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0ee80a0b); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf9d8ef82); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf6a00800); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 8700000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0007ffff); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe1ffd4); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0010007a); - cx25840_write4(client, DIF_BPF_COEFF89, 0x007cffb2); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec6ff10); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x00e60277); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0168fdf9); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fe50); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x03ce0631); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0188f9c8); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7c7ff43); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x091509e3); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xff39f3f6); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf52d02ea); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0ea30ac9); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfa9bef95); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf64607d0); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 8800000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00090007); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe9ffca); - cx25840_write4(client, DIF_BPF_COEFF67, 0xfff00065); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00a10003); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfee6feb6); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x0053025b); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0213fed0); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fd46); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x02c70668); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x02eafadb); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf74bfdae); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x08230a9c); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c7f4a3); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf45b01a6); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0e480b7c); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfb61efae); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf5ef079f); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 8900000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0008000d); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfff5ffc8); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffd10043); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00b20053); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff24fe7c); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xffb9020c); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0295ffbb); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fc64); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x019b0654); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x042dfc1c); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf714fc2a); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x07020b21); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0251f575); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3a7005e); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0dd80c24); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfc2aefcd); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf599076e); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 9000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00060011); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0002ffcf); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffba0018); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00ad009a); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff79fe68); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xff260192); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02e500ab); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fbb6); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x005b05f7); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0545fd81); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf723fabf); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x05b80b70); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d2f669); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf313ff15); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0d550cbf); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfcf6eff2); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf544073d); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 9100000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00030012); - cx25840_write4(client, DIF_BPF_COEFF45, 0x000fffdd); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffacffea); - cx25840_write4(client, DIF_BPF_COEFF89, 0x009300cf); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xffdcfe7c); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfea600f7); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02fd0190); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fb46); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xff150554); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0627fefd); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf778f978); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x044d0b87); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0543f77d); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2a0fdcf); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0cbe0d4e); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfdc4f01d); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4f2070b); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 9200000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00000010); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001afff0); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffaaffbf); - cx25840_write4(client, DIF_BPF_COEFF89, 0x006700ed); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0043feb6); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe460047); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02db0258); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fb1b); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfddc0473); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x06c90082); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf811f85e); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x02c90b66); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x069ff8ad); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf250fc8d); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0c140dcf); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfe93f04d); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4a106d9); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 9300000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffc000c); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00200006); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffb4ff9c); - cx25840_write4(client, DIF_BPF_COEFF89, 0x002f00ef); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00a4ff10); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe0dff92); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x028102f7); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fb37); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfcbf035e); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x07260202); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf8e8f778); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x01340b0d); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e1f9f4); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf223fb51); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0b590e42); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xff64f083); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf45206a7); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 9400000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff90005); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0022001a); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9ff86); - cx25840_write4(client, DIF_BPF_COEFF89, 0xfff000d7); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00f2ff82); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe01fee5); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x01f60362); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fb99); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfbcc0222); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x07380370); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf9f7f6cc); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xff990a7e); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0902fb50); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf21afa1f); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0a8d0ea6); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0034f0bf); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4050675); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 9500000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fffe); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001e002b); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5ff81); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffb400a5); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x01280000); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe24fe50); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x01460390); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfc3a); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb1000ce); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x070104bf); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb37f65f); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe0009bc); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a00fcbb); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf235f8f8); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x09b20efc); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0105f101); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf3ba0642); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 9600000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0001ffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff7); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00150036); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0005ff8c); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff810061); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x013d007e); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe71fddf); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x007c0380); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fd13); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa94ff70); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x068005e2); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfc9bf633); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfc7308ca); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad5fe30); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf274f7e0); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x08c90f43); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x01d4f147); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf371060f); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 9700000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fff1); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00090038); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0025ffa7); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff5e0012); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x013200f0); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfee3fd9b); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xffaa0331); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0311fe15); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa60fe18); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x05bd06d1); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfe1bf64a); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfafa07ae); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7effab); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2d5f6d7); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x07d30f7a); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x02a3f194); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf32905dc); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 9800000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffcffee); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfffb0032); - cx25840_write4(client, DIF_BPF_COEFF67, 0x003fffcd); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff4effc1); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0106014a); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xff6efd8a); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfedd02aa); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b0ff34); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa74fcd7); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x04bf0781); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xffaaf6a3); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf99e066b); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf90128); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf359f5e1); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x06d20fa2); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0370f1e5); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2e405a8); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 9900000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0xffffffee); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffef0024); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0051fffa); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff54ff77); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00be0184); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x0006fdad); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe2701f3); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413005e); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfad1fbba); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x039007ee); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x013bf73d); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf868050a); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c4302a1); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3fdf4fe); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x05c70fba); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x043bf23c); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2a10575); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 10000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0003fff1); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe50011); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00570027); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff70ff3c); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00620198); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x009efe01); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd95011a); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x04350183); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb71fad0); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x023c0812); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x02c3f811); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf75e0390); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c5c0411); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf4c1f432); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x04b30fc1); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0503f297); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2610541); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 10100000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0006fff7); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffdffffc); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00510050); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff9dff18); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfffc0184); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x0128fe80); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd32002e); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x04130292); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc4dfa21); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x00d107ee); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x0435f91c); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf6850205); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c430573); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf5a1f37d); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x03990fba); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x05c7f2f8); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf222050d); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 10200000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0008fffe); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffdfffe7); - cx25840_write4(client, DIF_BPF_COEFF67, 0x003f006e); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffd6ff0f); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff96014a); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x0197ff1f); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd05ff3e); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b0037c); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd59f9b7); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xff5d0781); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x0585fa56); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf5e4006f); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf906c4); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf69df2e0); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x02790fa2); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0688f35d); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf1e604d8); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 10300000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00090005); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe4ffd6); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0025007e); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0014ff20); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff3c00f0); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01e1ffd0); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd12fe5c); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x03110433); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe88f996); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfdf106d1); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x06aafbb7); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf57efed8); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7e07ff); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf7b0f25e); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x01560f7a); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0745f3c7); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf1ac04a4); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 10400000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0008000c); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffedffcb); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0005007d); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0050ff4c); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfef6007e); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01ff0086); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd58fd97); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x024104ad); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xffcaf9c0); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfc9905e2); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x079afd35); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf555fd46); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad50920); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf8d9f1f6); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x00310f43); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x07fdf435); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf174046f); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 10500000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00050011); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfffaffc8); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5006b); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0082ff8c); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfecc0000); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01f00130); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdd2fcfc); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d04e3); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x010efa32); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfb6404bf); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x084efec5); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf569fbc2); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a000a23); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfa15f1ab); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xff0b0efc); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x08b0f4a7); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf13f043a); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 10600000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00020012); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0007ffcd); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9004c); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00a4ffd9); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec3ff82); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01b401c1); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe76fc97); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x004404d2); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0245fae8); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa5f0370); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x08c1005f); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf5bcfa52); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x09020b04); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfb60f17b); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xfde70ea6); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x095df51e); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf10c0405); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 10700000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xffff0011); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0014ffdb); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffb40023); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00b2002a); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfedbff10); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x0150022d); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xff38fc6f); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36047b); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x035efbda); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9940202); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x08ee01f5); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf649f8fe); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e10bc2); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfcb6f169); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xfcc60e42); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0a04f599); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0db03d0); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 10800000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffb000d); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001dffed); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffaafff5); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00aa0077); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff13feb6); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x00ce026b); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x000afc85); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe3503e3); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x044cfcfb); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf90c0082); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x08d5037f); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf710f7cc); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x069f0c59); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfe16f173); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xfbaa0dcf); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0aa5f617); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0ad039b); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 10900000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff90006); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00210003); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffacffc8); - cx25840_write4(client, DIF_BPF_COEFF89, 0x008e00b6); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff63fe7c); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x003a0275); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x00dafcda); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd510313); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0501fe40); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8cbfefd); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x087604f0); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf80af6c2); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x05430cc8); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xff7af19a); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xfa940d4e); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0b3ff699); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0810365); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 11000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0001ffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8ffff); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00210018); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffbaffa3); - cx25840_write4(client, DIF_BPF_COEFF89, 0x006000e1); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xffc4fe68); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xffa0024b); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x019afd66); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc990216); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0575ff99); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8d4fd81); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x07d40640); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf932f5e6); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d20d0d); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x00dff1de); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf9860cbf); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0bd1f71e); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf058032f); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 11100000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff8); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001b0029); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffd1ff8a); - cx25840_write4(client, DIF_BPF_COEFF89, 0x002600f2); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x002cfe7c); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xff0f01f0); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x023bfe20); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc1700fa); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x05a200f7); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf927fc1c); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x06f40765); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfa82f53b); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x02510d27); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0243f23d); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf8810c24); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0c5cf7a7); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf03102fa); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 11200000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffafff2); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00110035); - cx25840_write4(client, DIF_BPF_COEFF67, 0xfff0ff81); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffe700e7); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x008ffeb6); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe94016d); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02b0fefb); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3ffd1); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x05850249); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9c1fadb); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x05de0858); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfbf2f4c4); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c70d17); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x03a0f2b8); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf7870b7c); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0cdff833); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf00d02c4); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 11300000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffdffee); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00040038); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0010ff88); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffac00c2); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00e2ff10); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe3900cb); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f1ffe9); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3feaa); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x05210381); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa9cf9c8); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x04990912); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfd7af484); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xff390cdb); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x04f4f34d); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf69a0ac9); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0d5af8c1); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xefec028e); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 11400000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0000ffee); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfff60033); - cx25840_write4(client, DIF_BPF_COEFF67, 0x002fff9f); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff7b0087); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x011eff82); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe080018); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f900d8); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fd96); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x04790490); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfbadf8ed); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x032f098e); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xff10f47d); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaf0c75); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x063cf3fc); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf5ba0a0b); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0dccf952); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xefcd0258); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 11500000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0004fff1); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffea0026); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0046ffc3); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff5a003c); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x013b0000); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe04ff63); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02c801b8); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fca6); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0397056a); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfcecf853); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x01ad09c9); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x00acf4ad); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2e0be7); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0773f4c2); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf4e90943); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0e35f9e6); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xefb10221); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 11600000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0007fff6); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe20014); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0054ffee); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff4effeb); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0137007e); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe2efebb); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0260027a); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fbe6); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x02870605); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfe4af7fe); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x001d09c1); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0243f515); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabd0b32); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0897f59e); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf4280871); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0e95fa7c); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef9701eb); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 11700000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0008fffd); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffff); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0056001d); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff57ff9c); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x011300f0); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe82fe2e); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x01ca0310); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fb62); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0155065a); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xffbaf7f2); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfe8c0977); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x03cef5b2); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf9610a58); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x09a5f68f); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf3790797); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0eebfb14); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef8001b5); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 11800000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00080004); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe0ffe9); - cx25840_write4(client, DIF_BPF_COEFF67, 0x004c0047); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff75ff58); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00d1014a); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfef9fdc8); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0111036f); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fb21); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x00120665); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x012df82e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfd0708ec); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0542f682); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81f095c); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0a9af792); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf2db06b5); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0f38fbad); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef6c017e); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 11900000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0007000b); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe7ffd8); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00370068); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffa4ff28); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00790184); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xff87fd91); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x00430392); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fb26); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfece0626); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0294f8b2); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb990825); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0698f77f); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fe0842); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0b73f8a7); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf25105cd); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0f7bfc48); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef5a0148); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 12000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00050010); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfff2ffcc); - cx25840_write4(client, DIF_BPF_COEFF67, 0x001b007b); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffdfff10); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00140198); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x0020fd8e); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xff710375); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfb73); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd9a059f); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x03e0f978); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfa4e0726); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x07c8f8a7); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600070c); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0c2ff9c9); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf1db04de); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0fb4fce5); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef4b0111); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 12100000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00010012); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffffffc8); - cx25840_write4(client, DIF_BPF_COEFF67, 0xfffb007e); - cx25840_write4(client, DIF_BPF_COEFF89, 0x001dff14); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xffad0184); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x00b7fdbe); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfea9031b); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fc01); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc8504d6); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0504fa79); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf93005f6); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x08caf9f2); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf52b05c0); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0ccbfaf9); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf17903eb); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0fe3fd83); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef3f00db); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 12200000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffe0011); - cx25840_write4(client, DIF_BPF_COEFF45, 0x000cffcc); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffdb0071); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0058ff32); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff4f014a); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x013cfe1f); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdfb028a); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0311fcc9); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb9d03d6); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x05f4fbad); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf848049d); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0999fb5b); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf4820461); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d46fc32); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf12d02f4); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x1007fe21); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef3600a4); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 12300000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffa000e); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0017ffd9); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffc10055); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0088ff68); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff0400f0); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01a6fea7); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd7501cc); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b0fdc0); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfaef02a8); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x06a7fd07); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf79d0326); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a31fcda); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf40702f3); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d9ffd72); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0f601fa); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x1021fec0); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef2f006d); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 12400000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0001ffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80007); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001fffeb); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffaf002d); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00a8ffb0); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed3007e); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01e9ff4c); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd2000ee); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413fed8); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa82015c); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0715fe7d); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7340198); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a8dfe69); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3bd017c); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0dd5feb8); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0d500fd); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x1031ff60); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef2b0037); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 12500000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff70000); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00220000); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffa90000); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00b30000); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec20000); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x02000000); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd030000); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x04350000); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa5e0000); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x073b0000); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7110000); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0aac0000); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3a40000); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0de70000); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0c90000); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x10360000); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef290000); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 12600000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff9); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001f0015); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffafffd3); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00a80050); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed3ff82); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01e900b4); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd20ff12); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x04130128); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa82fea4); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x07150183); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf734fe68); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a8d0197); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3bdfe84); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0dd50148); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0d5ff03); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x103100a0); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef2bffc9); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 12700000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffafff2); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00170027); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffc1ffab); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00880098); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff04ff10); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01a60159); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd75fe34); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b00240); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfaeffd58); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x06a702f9); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf79dfcda); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a310326); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf407fd0d); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d9f028e); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0f6fe06); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x10210140); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef2fff93); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 12800000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffeffef); - cx25840_write4(client, DIF_BPF_COEFF45, 0x000c0034); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffdbff8f); - cx25840_write4(client, DIF_BPF_COEFF89, 0x005800ce); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff4ffeb6); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x013c01e1); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdfbfd76); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x03110337); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb9dfc2a); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x05f40453); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf848fb63); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x099904a5); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf482fb9f); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d4603ce); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf12dfd0c); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x100701df); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef36ff5c); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 12900000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0001ffee); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffff0038); - cx25840_write4(client, DIF_BPF_COEFF67, 0xfffbff82); - cx25840_write4(client, DIF_BPF_COEFF89, 0x001d00ec); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xffadfe7c); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x00b70242); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfea9fce5); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x024103ff); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc85fb2a); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x05040587); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf930fa0a); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x08ca060e); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf52bfa40); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0ccb0507); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf179fc15); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0fe3027d); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef3fff25); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 13000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0005fff0); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfff20034); - cx25840_write4(client, DIF_BPF_COEFF67, 0x001bff85); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffdf00f0); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0014fe68); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x00200272); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xff71fc8b); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d048d); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd9afa61); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x03e00688); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfa4ef8da); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x07c80759); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600f8f4); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0c2f0637); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf1dbfb22); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0fb4031b); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef4bfeef); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 13100000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0007fff5); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe70028); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0037ff98); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffa400d8); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0079fe7c); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xff87026f); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0043fc6e); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x004404da); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfecef9da); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0294074e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb99f7db); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x06980881); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fef7be); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0b730759); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf251fa33); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0f7b03b8); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef5afeb8); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 13200000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0008fffc); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe00017); - cx25840_write4(client, DIF_BPF_COEFF67, 0x004cffb9); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff7500a8); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00d1feb6); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfef90238); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0111fc91); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xff3604df); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0012f99b); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x012d07d2); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfd07f714); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0542097e); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81ff6a4); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0a9a086e); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf2dbf94b); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0f380453); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef6cfe82); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 13300000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00080003); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffde0001); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0056ffe3); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff570064); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0113ff10); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe8201d2); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x01cafcf0); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35049e); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0155f9a6); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xffba080e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfe8cf689); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x03ce0a4e); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf961f5a8); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x09a50971); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf379f869); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0eeb04ec); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef80fe4b); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 13400000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0007000a); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe2ffec); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00540012); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff4e0015); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0137ff82); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe2e0145); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0260fd86); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51041a); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0287f9fb); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfe4a0802); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x001df63f); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x02430aeb); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabdf4ce); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x08970a62); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf428f78f); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0e950584); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef97fe15); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 13500000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0004000f); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffeaffda); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0046003d); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff5affc4); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x013b0000); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe04009d); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02c8fe48); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99035a); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0397fa96); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfcec07ad); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x01adf637); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x00ac0b53); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2ef419); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x07730b3e); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf4e9f6bd); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0e35061a); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xefb1fddf); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 13600000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00000012); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfff6ffcd); - cx25840_write4(client, DIF_BPF_COEFF67, 0x002f0061); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff7bff79); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x011e007e); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe08ffe8); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f9ff28); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17026a); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0479fb70); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfbad0713); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x032ff672); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xff100b83); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaff38b); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x063c0c04); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf5baf5f5); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0dcc06ae); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xefcdfda8); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 13700000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffd0012); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0004ffc8); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00100078); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffacff3e); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00e200f0); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe39ff35); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f10017); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd30156); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0521fc7f); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa9c0638); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x0499f6ee); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfd7a0b7c); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xff39f325); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x04f40cb3); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf69af537); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0d5a073f); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xefecfd72); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 13800000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0001fffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffa000e); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0011ffcb); - cx25840_write4(client, DIF_BPF_COEFF67, 0xfff0007f); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffe7ff19); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x008f014a); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe94fe93); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02b00105); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3002f); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0585fdb7); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9c10525); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x05def7a8); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfbf20b3c); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c7f2e9); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x03a00d48); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf787f484); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0cdf07cd); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf00dfd3c); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 13900000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80008); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001bffd7); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffd10076); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0026ff0e); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x002c0184); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xff0ffe10); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x023b01e0); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17ff06); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x05a2ff09); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf92703e4); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x06f4f89b); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfa820ac5); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0251f2d9); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x02430dc3); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf881f3dc); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0c5c0859); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf031fd06); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 14000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80001); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0021ffe8); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffba005d); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0060ff1f); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xffc40198); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xffa0fdb5); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x019a029a); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fdea); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x05750067); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8d4027f); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x07d4f9c0); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf9320a1a); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d2f2f3); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x00df0e22); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf986f341); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0bd108e2); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf058fcd1); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 14100000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fffa); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0021fffd); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffac0038); - cx25840_write4(client, DIF_BPF_COEFF89, 0x008eff4a); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff630184); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x003afd8b); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x00da0326); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fced); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x050101c0); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8cb0103); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x0876fb10); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf80a093e); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0543f338); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xff7a0e66); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xfa94f2b2); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0b3f0967); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf081fc9b); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 14200000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffbfff3); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001d0013); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffaa000b); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00aaff89); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff13014a); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x00cefd95); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x000a037b); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fc1d); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x044c0305); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf90cff7e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x08d5fc81); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf7100834); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x069ff3a7); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfe160e8d); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xfbaaf231); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0aa509e9); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0adfc65); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 14300000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0xffffffef); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00140025); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffb4ffdd); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00b2ffd6); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfedb00f0); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x0150fdd3); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xff380391); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fb85); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x035e0426); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf994fdfe); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x08eefe0b); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf6490702); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e1f43e); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfcb60e97); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xfcc6f1be); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0a040a67); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0dbfc30); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 14400000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0002ffee); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00070033); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9ffb4); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00a40027); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec3007e); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01b4fe3f); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe760369); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fb2e); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x02450518); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa5ffc90); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x08c1ffa1); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf5bc05ae); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0902f4fc); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfb600e85); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xfde7f15a); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x095d0ae2); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf10cfbfb); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 14500000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0002); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0005ffef); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfffa0038); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5ff95); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00820074); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfecc0000); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01f0fed0); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdd20304); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfb1d); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x010e05ce); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfb64fb41); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x084e013b); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf569043e); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a00f5dd); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfa150e55); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xff0bf104); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x08b00b59); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf13ffbc6); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 14600000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0008fff4); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffed0035); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0005ff83); - cx25840_write4(client, DIF_BPF_COEFF89, 0x005000b4); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfef6ff82); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01ffff7a); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd580269); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fb53); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xffca0640); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfc99fa1e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x079a02cb); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf55502ba); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad5f6e0); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf8d90e0a); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0031f0bd); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x07fd0bcb); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf174fb91); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 14700000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0009fffb); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe4002a); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0025ff82); - cx25840_write4(client, DIF_BPF_COEFF89, 0x001400e0); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff3cff10); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01e10030); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd1201a4); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0311fbcd); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe88066a); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfdf1f92f); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x06aa0449); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf57e0128); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7ef801); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf7b00da2); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0156f086); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x07450c39); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf1acfb5c); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 14800000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00080002); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffdf0019); - cx25840_write4(client, DIF_BPF_COEFF67, 0x003fff92); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffd600f1); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff96feb6); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x019700e1); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd0500c2); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b0fc84); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd590649); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xff5df87f); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x058505aa); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf5e4ff91); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf9f93c); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf69d0d20); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0279f05e); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x06880ca3); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf1e6fb28); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 14900000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00060009); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffdf0004); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0051ffb0); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff9d00e8); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfffcfe7c); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01280180); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd32ffd2); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413fd6e); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc4d05df); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x00d1f812); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x043506e4); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf685fdfb); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c43fa8d); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf5a10c83); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0399f046); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x05c70d08); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf222faf3); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 15000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0003000f); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe5ffef); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0057ffd9); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff7000c4); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0062fe68); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x009e01ff); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd95fee6); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0435fe7d); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb710530); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x023cf7ee); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x02c307ef); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf75efc70); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c5cfbef); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf4c10bce); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x04b3f03f); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x05030d69); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf261fabf); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 15100000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xffff0012); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffefffdc); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00510006); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff540089); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00befe7c); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x00060253); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe27fe0d); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413ffa2); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfad10446); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0390f812); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x013b08c3); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf868faf6); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c43fd5f); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3fd0b02); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x05c7f046); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x043b0dc4); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2a1fa8b); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 15200000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0001fffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffc0012); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfffbffce); - cx25840_write4(client, DIF_BPF_COEFF67, 0x003f0033); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff4e003f); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0106feb6); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xff6e0276); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfeddfd56); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b000cc); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa740329); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x04bff87f); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xffaa095d); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf99ef995); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf9fed8); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3590a1f); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x06d2f05e); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x03700e1b); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2e4fa58); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 15300000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0001ffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9000f); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0009ffc8); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00250059); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff5effee); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0132ff10); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfee30265); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xffaafccf); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x031101eb); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa6001e8); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x05bdf92f); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfe1b09b6); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfafaf852); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7e0055); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2d50929); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x07d3f086); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x02a30e6c); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf329fa24); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 15400000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80009); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0015ffca); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00050074); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff81ff9f); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x013dff82); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe710221); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x007cfc80); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x024102ed); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa940090); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0680fa1e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfc9b09cd); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfc73f736); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad501d0); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2740820); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x08c9f0bd); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x01d40eb9); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf371f9f1); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 15500000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80002); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001effd5); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5007f); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffb4ff5b); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x01280000); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe2401b0); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0146fc70); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d03c6); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb10ff32); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0701fb41); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb3709a1); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe00f644); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a000345); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2350708); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x09b2f104); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x01050eff); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf3baf9be); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 15600000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fffb); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0022ffe6); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9007a); - cx25840_write4(client, DIF_BPF_COEFF89, 0xfff0ff29); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00f2007e); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe01011b); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x01f6fc9e); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x00440467); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfbccfdde); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0738fc90); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf9f70934); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xff99f582); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x090204b0); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf21a05e1); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0a8df15a); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x00340f41); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf405f98b); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 15700000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffcfff4); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0020fffa); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffb40064); - cx25840_write4(client, DIF_BPF_COEFF89, 0x002fff11); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00a400f0); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe0d006e); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0281fd09); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xff3604c9); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfcbffca2); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0726fdfe); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf8e80888); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0134f4f3); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e1060c); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf22304af); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0b59f1be); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xff640f7d); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf452f959); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 15800000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0000fff0); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001a0010); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffaa0041); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0067ff13); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0043014a); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe46ffb9); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02dbfda8); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe3504e5); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfddcfb8d); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x06c9ff7e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf81107a2); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x02c9f49a); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x069f0753); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2500373); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0c14f231); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfe930fb3); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4a1f927); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 15900000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0002); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0003ffee); - cx25840_write4(client, DIF_BPF_COEFF45, 0x000f0023); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffac0016); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0093ff31); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xffdc0184); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfea6ff09); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02fdfe70); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd5104ba); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xff15faac); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x06270103); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7780688); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x044df479); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x05430883); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2a00231); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0cbef2b2); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfdc40fe3); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4f2f8f5); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 16000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0006ffef); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00020031); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffbaffe8); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00adff66); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff790198); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xff26fe6e); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02e5ff55); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99044a); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x005bfa09); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0545027f); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7230541); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x05b8f490); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d20997); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf31300eb); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0d55f341); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfcf6100e); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf544f8c3); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - } -} - -static void cx23888_std_setup(struct i2c_client *client) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - v4l2_std_id std = state->std; - u32 ifHz; - - cx25840_write4(client, 0x478, 0x6628021F); - cx25840_write4(client, 0x400, 0x0); - cx25840_write4(client, 0x4b4, 0x20524030); - cx25840_write4(client, 0x47c, 0x010a8263); - - if (std & V4L2_STD_NTSC) { - v4l_dbg(1, cx25840_debug, client, "%s() Selecting NTSC", - __func__); - - /* Horiz / vert timing */ - cx25840_write4(client, 0x428, 0x1e1e601a); - cx25840_write4(client, 0x424, 0x5b2d007a); - - /* DIF NTSC */ - cx25840_write4(client, 0x304, 0x6503bc0c); - cx25840_write4(client, 0x308, 0xbd038c85); - cx25840_write4(client, 0x30c, 0x1db4640a); - cx25840_write4(client, 0x310, 0x00008800); - cx25840_write4(client, 0x314, 0x44400400); - cx25840_write4(client, 0x32c, 0x0c800800); - cx25840_write4(client, 0x330, 0x27000100); - cx25840_write4(client, 0x334, 0x1f296e1f); - cx25840_write4(client, 0x338, 0x009f50c1); - cx25840_write4(client, 0x340, 0x1befbf06); - cx25840_write4(client, 0x344, 0x000035e8); - - /* DIF I/F */ - ifHz = 5400000; - - } else { - v4l_dbg(1, cx25840_debug, client, "%s() Selecting PAL-BG", - __func__); - - /* Horiz / vert timing */ - cx25840_write4(client, 0x428, 0x28244024); - cx25840_write4(client, 0x424, 0x5d2d0084); - - /* DIF */ - cx25840_write4(client, 0x304, 0x6503bc0c); - cx25840_write4(client, 0x308, 0xbd038c85); - cx25840_write4(client, 0x30c, 0x1db4640a); - cx25840_write4(client, 0x310, 0x00008800); - cx25840_write4(client, 0x314, 0x44400600); - cx25840_write4(client, 0x32c, 0x0c800800); - cx25840_write4(client, 0x330, 0x27000100); - cx25840_write4(client, 0x334, 0x213530ec); - cx25840_write4(client, 0x338, 0x00a65ba8); - cx25840_write4(client, 0x340, 0x1befbf06); - cx25840_write4(client, 0x344, 0x000035e8); - - /* DIF I/F */ - ifHz = 6000000; - } - - cx23885_dif_setup(client, ifHz); - - /* Explicitly ensure the inputs are reconfigured after - * a standard change. - */ - set_input(client, state->vid_input, state->aud_input); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_ctrl_ops cx25840_ctrl_ops = { - .s_ctrl = cx25840_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops cx25840_core_ops = { - .log_status = cx25840_log_status, - .g_chip_ident = cx25840_g_chip_ident, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, - .s_std = cx25840_s_std, - .g_std = cx25840_g_std, - .reset = cx25840_reset, - .load_fw = cx25840_load_fw, - .s_io_pin_config = common_s_io_pin_config, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = cx25840_g_register, - .s_register = cx25840_s_register, -#endif - .interrupt_service_routine = cx25840_irq_handler, -}; - -static const struct v4l2_subdev_tuner_ops cx25840_tuner_ops = { - .s_frequency = cx25840_s_frequency, - .s_radio = cx25840_s_radio, - .g_tuner = cx25840_g_tuner, - .s_tuner = cx25840_s_tuner, -}; - -static const struct v4l2_subdev_audio_ops cx25840_audio_ops = { - .s_clock_freq = cx25840_s_clock_freq, - .s_routing = cx25840_s_audio_routing, - .s_stream = cx25840_s_audio_stream, -}; - -static const struct v4l2_subdev_video_ops cx25840_video_ops = { - .s_routing = cx25840_s_video_routing, - .s_mbus_fmt = cx25840_s_mbus_fmt, - .s_stream = cx25840_s_stream, - .g_input_status = cx25840_g_input_status, -}; - -static const struct v4l2_subdev_vbi_ops cx25840_vbi_ops = { - .decode_vbi_line = cx25840_decode_vbi_line, - .s_raw_fmt = cx25840_s_raw_fmt, - .s_sliced_fmt = cx25840_s_sliced_fmt, - .g_sliced_fmt = cx25840_g_sliced_fmt, -}; - -static const struct v4l2_subdev_ops cx25840_ops = { - .core = &cx25840_core_ops, - .tuner = &cx25840_tuner_ops, - .audio = &cx25840_audio_ops, - .video = &cx25840_video_ops, - .vbi = &cx25840_vbi_ops, - .ir = &cx25840_ir_ops, -}; - -/* ----------------------------------------------------------------------- */ - -static u32 get_cx2388x_ident(struct i2c_client *client) -{ - u32 ret; - - /* Come out of digital power down */ - cx25840_write(client, 0x000, 0); - - /* Detecting whether the part is cx23885/7/8 is more - * difficult than it needs to be. No ID register. Instead we - * probe certain registers indicated in the datasheets to look - * for specific defaults that differ between the silicon designs. */ - - /* It's either 885/7 if the IR Tx Clk Divider register exists */ - if (cx25840_read4(client, 0x204) & 0xffff) { - /* CX23885 returns bogus repetitive byte values for the DIF, - * which doesn't exist for it. (Ex. 8a8a8a8a or 31313131) */ - ret = cx25840_read4(client, 0x300); - if (((ret & 0xffff0000) >> 16) == (ret & 0xffff)) { - /* No DIF */ - ret = V4L2_IDENT_CX23885_AV; - } else { - /* CX23887 has a broken DIF, but the registers - * appear valid (but unused), good enough to detect. */ - ret = V4L2_IDENT_CX23887_AV; - } - } else if (cx25840_read4(client, 0x300) & 0x0fffffff) { - /* DIF PLL Freq Word reg exists; chip must be a CX23888 */ - ret = V4L2_IDENT_CX23888_AV; - } else { - v4l_err(client, "Unable to detect h/w, assuming cx23887\n"); - ret = V4L2_IDENT_CX23887_AV; - } - - /* Back into digital power down */ - cx25840_write(client, 0x000, 2); - return ret; -} - -static int cx25840_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct cx25840_state *state; - struct v4l2_subdev *sd; - int default_volume; - u32 id = V4L2_IDENT_NONE; - u16 device_id; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - v4l_dbg(1, cx25840_debug, client, "detecting cx25840 client on address 0x%x\n", client->addr << 1); - - device_id = cx25840_read(client, 0x101) << 8; - device_id |= cx25840_read(client, 0x100); - v4l_dbg(1, cx25840_debug, client, "device_id = 0x%04x\n", device_id); - - /* The high byte of the device ID should be - * 0x83 for the cx2583x and 0x84 for the cx2584x */ - if ((device_id & 0xff00) == 0x8300) { - id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6; - } else if ((device_id & 0xff00) == 0x8400) { - id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf); - } else if (device_id == 0x0000) { - id = get_cx2388x_ident(client); - } else if ((device_id & 0xfff0) == 0x5A30) { - /* The CX23100 (0x5A3C = 23100) doesn't have an A/V decoder */ - id = V4L2_IDENT_CX2310X_AV; - } else if ((device_id & 0xff) == (device_id >> 8)) { - v4l_err(client, - "likely a confused/unresponsive cx2388[578] A/V decoder" - " found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - v4l_err(client, "A method to reset it from the cx25840 driver" - " software is not known at this time\n"); - return -ENODEV; - } else { - v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n"); - return -ENODEV; - } - - state = kzalloc(sizeof(struct cx25840_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &cx25840_ops); - - switch (id) { - case V4L2_IDENT_CX23885_AV: - v4l_info(client, "cx23885 A/V decoder found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - break; - case V4L2_IDENT_CX23887_AV: - v4l_info(client, "cx23887 A/V decoder found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - break; - case V4L2_IDENT_CX23888_AV: - v4l_info(client, "cx23888 A/V decoder found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - break; - case V4L2_IDENT_CX2310X_AV: - v4l_info(client, "cx%d A/V decoder found @ 0x%x (%s)\n", - device_id, client->addr << 1, client->adapter->name); - break; - case V4L2_IDENT_CX25840: - case V4L2_IDENT_CX25841: - case V4L2_IDENT_CX25842: - case V4L2_IDENT_CX25843: - /* Note: revision '(device_id & 0x0f) == 2' was never built. The - marking skips from 0x1 == 22 to 0x3 == 23. */ - v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n", - (device_id & 0xfff0) >> 4, - (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 - : (device_id & 0x0f), - client->addr << 1, client->adapter->name); - break; - case V4L2_IDENT_CX25836: - case V4L2_IDENT_CX25837: - default: - v4l_info(client, "cx25%3x-%x found @ 0x%x (%s)\n", - (device_id & 0xfff0) >> 4, device_id & 0x0f, - client->addr << 1, client->adapter->name); - break; - } - - state->c = client; - state->vid_input = CX25840_COMPOSITE7; - state->aud_input = CX25840_AUDIO8; - state->audclk_freq = 48000; - state->audmode = V4L2_TUNER_MODE_LANG1; - state->vbi_line_offset = 8; - state->id = id; - state->rev = device_id; - v4l2_ctrl_handler_init(&state->hdl, 9); - v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); - v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, - V4L2_CID_CONTRAST, 0, 127, 1, 64); - v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, - V4L2_CID_SATURATION, 0, 127, 1, 64); - v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, - V4L2_CID_HUE, -128, 127, 1, 0); - if (!is_cx2583x(state)) { - default_volume = cx25840_read(client, 0x8d4); - /* - * Enforce the legacy PVR-350/MSP3400 to PVR-150/CX25843 volume - * scale mapping limits to avoid -ERANGE errors when - * initializing the volume control - */ - if (default_volume > 228) { - /* Bottom out at -96 dB, v4l2 vol range 0x2e00-0x2fff */ - default_volume = 228; - cx25840_write(client, 0x8d4, 228); - } - else if (default_volume < 20) { - /* Top out at + 8 dB, v4l2 vol range 0xfe00-0xffff */ - default_volume = 20; - cx25840_write(client, 0x8d4, 20); - } - default_volume = (((228 - default_volume) >> 1) + 23) << 9; - - state->volume = v4l2_ctrl_new_std(&state->hdl, - &cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_VOLUME, - 0, 65535, 65535 / 100, default_volume); - state->mute = v4l2_ctrl_new_std(&state->hdl, - &cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_MUTE, - 0, 1, 1, 0); - v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops, - V4L2_CID_AUDIO_BALANCE, - 0, 65535, 65535 / 100, 32768); - v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops, - V4L2_CID_AUDIO_BASS, - 0, 65535, 65535 / 100, 32768); - v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops, - V4L2_CID_AUDIO_TREBLE, - 0, 65535, 65535 / 100, 32768); - } - sd->ctrl_handler = &state->hdl; - if (state->hdl.error) { - int err = state->hdl.error; - - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return err; - } - if (!is_cx2583x(state)) - v4l2_ctrl_cluster(2, &state->volume); - v4l2_ctrl_handler_setup(&state->hdl); - - if (client->dev.platform_data) { - struct cx25840_platform_data *pdata = client->dev.platform_data; - - state->pvr150_workaround = pdata->pvr150_workaround; - } - - cx25840_ir_probe(sd); - return 0; -} - -static int cx25840_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct cx25840_state *state = to_state(sd); - - cx25840_ir_remove(sd); - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return 0; -} - -static const struct i2c_device_id cx25840_id[] = { - { "cx25840", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, cx25840_id); - -static struct i2c_driver cx25840_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "cx25840", - }, - .probe = cx25840_probe, - .remove = cx25840_remove, - .id_table = cx25840_id, -}; - -module_i2c_driver(cx25840_driver); diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h deleted file mode 100644 index bd4ada28b49..00000000000 --- a/drivers/media/video/cx25840/cx25840-core.h +++ /dev/null @@ -1,137 +0,0 @@ -/* cx25840 internal API header - * - * Copyright (C) 2003-2004 Chris Kennedy - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef _CX25840_CORE_H_ -#define _CX25840_CORE_H_ - - -#include -#include -#include -#include -#include - -struct cx25840_ir_state; - -struct cx25840_state { - struct i2c_client *c; - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - struct { - /* volume cluster */ - struct v4l2_ctrl *volume; - struct v4l2_ctrl *mute; - }; - int pvr150_workaround; - int radio; - v4l2_std_id std; - enum cx25840_video_input vid_input; - enum cx25840_audio_input aud_input; - u32 audclk_freq; - int audmode; - int vbi_line_offset; - u32 id; - u32 rev; - int is_initialized; - wait_queue_head_t fw_wait; /* wake up when the fw load is finished */ - struct work_struct fw_work; /* work entry for fw load */ - struct cx25840_ir_state *ir_state; -}; - -static inline struct cx25840_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct cx25840_state, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct cx25840_state, hdl)->sd; -} - -static inline bool is_cx2583x(struct cx25840_state *state) -{ - return state->id == V4L2_IDENT_CX25836 || - state->id == V4L2_IDENT_CX25837; -} - -static inline bool is_cx231xx(struct cx25840_state *state) -{ - return state->id == V4L2_IDENT_CX2310X_AV; -} - -static inline bool is_cx2388x(struct cx25840_state *state) -{ - return state->id == V4L2_IDENT_CX23885_AV || - state->id == V4L2_IDENT_CX23887_AV || - state->id == V4L2_IDENT_CX23888_AV; -} - -static inline bool is_cx23885(struct cx25840_state *state) -{ - return state->id == V4L2_IDENT_CX23885_AV; -} - -static inline bool is_cx23887(struct cx25840_state *state) -{ - return state->id == V4L2_IDENT_CX23887_AV; -} - -static inline bool is_cx23888(struct cx25840_state *state) -{ - return state->id == V4L2_IDENT_CX23888_AV; -} - -/* ----------------------------------------------------------------------- */ -/* cx25850-core.c */ -int cx25840_write(struct i2c_client *client, u16 addr, u8 value); -int cx25840_write4(struct i2c_client *client, u16 addr, u32 value); -u8 cx25840_read(struct i2c_client *client, u16 addr); -u32 cx25840_read4(struct i2c_client *client, u16 addr); -int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned mask, u8 value); -int cx25840_and_or4(struct i2c_client *client, u16 addr, u32 and_mask, - u32 or_value); -void cx25840_std_setup(struct i2c_client *client); - -/* ----------------------------------------------------------------------- */ -/* cx25850-firmware.c */ -int cx25840_loadfw(struct i2c_client *client); - -/* ----------------------------------------------------------------------- */ -/* cx25850-audio.c */ -void cx25840_audio_set_path(struct i2c_client *client); -int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq); - -extern const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops; - -/* ----------------------------------------------------------------------- */ -/* cx25850-vbi.c */ -int cx25840_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt); -int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt); -int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt); -int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi); - -/* ----------------------------------------------------------------------- */ -/* cx25850-ir.c */ -extern const struct v4l2_subdev_ir_ops cx25840_ir_ops; -int cx25840_ir_log_status(struct v4l2_subdev *sd); -int cx25840_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled); -int cx25840_ir_probe(struct v4l2_subdev *sd); -int cx25840_ir_remove(struct v4l2_subdev *sd); - -#endif diff --git a/drivers/media/video/cx25840/cx25840-firmware.c b/drivers/media/video/cx25840/cx25840-firmware.c deleted file mode 100644 index b3169f94ece..00000000000 --- a/drivers/media/video/cx25840/cx25840-firmware.c +++ /dev/null @@ -1,175 +0,0 @@ -/* cx25840 firmware functions - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include -#include -#include -#include -#include - -#include "cx25840-core.h" - -/* - * Mike Isely - The FWSEND parameter controls the - * size of the firmware chunks sent down the I2C bus to the chip. - * Previously this had been set to 1024 but unfortunately some I2C - * implementations can't transfer data in such big gulps. - * Specifically, the pvrusb2 driver has a hard limit of around 60 - * bytes, due to the encapsulation there of I2C traffic into USB - * messages. So we have to significantly reduce this parameter. - */ -#define FWSEND 48 - -#define FWDEV(x) &((x)->dev) - -static char *firmware = ""; - -module_param(firmware, charp, 0444); - -MODULE_PARM_DESC(firmware, "Firmware image to load"); - -static void start_fw_load(struct i2c_client *client) -{ - /* DL_ADDR_LB=0 DL_ADDR_HB=0 */ - cx25840_write(client, 0x800, 0x00); - cx25840_write(client, 0x801, 0x00); - // DL_MAP=3 DL_AUTO_INC=0 DL_ENABLE=1 - cx25840_write(client, 0x803, 0x0b); - /* AUTO_INC_DIS=1 */ - cx25840_write(client, 0x000, 0x20); -} - -static void end_fw_load(struct i2c_client *client) -{ - /* AUTO_INC_DIS=0 */ - cx25840_write(client, 0x000, 0x00); - /* DL_ENABLE=0 */ - cx25840_write(client, 0x803, 0x03); -} - -#define CX2388x_FIRMWARE "v4l-cx23885-avcore-01.fw" -#define CX231xx_FIRMWARE "v4l-cx231xx-avcore-01.fw" -#define CX25840_FIRMWARE "v4l-cx25840.fw" - -static const char *get_fw_name(struct i2c_client *client) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - - if (firmware[0]) - return firmware; - if (is_cx2388x(state)) - return CX2388x_FIRMWARE; - if (is_cx231xx(state)) - return CX231xx_FIRMWARE; - return CX25840_FIRMWARE; -} - -static int check_fw_load(struct i2c_client *client, int size) -{ - /* DL_ADDR_HB DL_ADDR_LB */ - int s = cx25840_read(client, 0x801) << 8; - s |= cx25840_read(client, 0x800); - - if (size != s) { - v4l_err(client, "firmware %s load failed\n", - get_fw_name(client)); - return -EINVAL; - } - - v4l_info(client, "loaded %s firmware (%d bytes)\n", - get_fw_name(client), size); - return 0; -} - -static int fw_write(struct i2c_client *client, const u8 *data, int size) -{ - if (i2c_master_send(client, data, size) < size) { - v4l_err(client, "firmware load i2c failure\n"); - return -ENOSYS; - } - - return 0; -} - -int cx25840_loadfw(struct i2c_client *client) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - const struct firmware *fw = NULL; - u8 buffer[FWSEND]; - const u8 *ptr; - const char *fwname = get_fw_name(client); - int size, retval; - int MAX_BUF_SIZE = FWSEND; - u32 gpio_oe = 0, gpio_da = 0; - - if (is_cx2388x(state)) { - /* Preserve the GPIO OE and output bits */ - gpio_oe = cx25840_read(client, 0x160); - gpio_da = cx25840_read(client, 0x164); - } - - if (is_cx231xx(state) && MAX_BUF_SIZE > 16) { - v4l_err(client, " Firmware download size changed to 16 bytes max length\n"); - MAX_BUF_SIZE = 16; /* cx231xx cannot accept more than 16 bytes at a time */ - } - - if (request_firmware(&fw, fwname, FWDEV(client)) != 0) { - v4l_err(client, "unable to open firmware %s\n", fwname); - return -EINVAL; - } - - start_fw_load(client); - - buffer[0] = 0x08; - buffer[1] = 0x02; - - size = fw->size; - ptr = fw->data; - while (size > 0) { - int len = min(MAX_BUF_SIZE - 2, size); - - memcpy(buffer + 2, ptr, len); - - retval = fw_write(client, buffer, len + 2); - - if (retval < 0) { - release_firmware(fw); - return retval; - } - - size -= len; - ptr += len; - } - - end_fw_load(client); - - size = fw->size; - release_firmware(fw); - - if (is_cx2388x(state)) { - /* Restore GPIO configuration after f/w load */ - cx25840_write(client, 0x160, gpio_oe); - cx25840_write(client, 0x164, gpio_da); - } - - return check_fw_load(client, size); -} - -MODULE_FIRMWARE(CX2388x_FIRMWARE); -MODULE_FIRMWARE(CX231xx_FIRMWARE); -MODULE_FIRMWARE(CX25840_FIRMWARE); - diff --git a/drivers/media/video/cx25840/cx25840-ir.c b/drivers/media/video/cx25840/cx25840-ir.c deleted file mode 100644 index 38ce76ed192..00000000000 --- a/drivers/media/video/cx25840/cx25840-ir.c +++ /dev/null @@ -1,1281 +0,0 @@ -/* - * Driver for the Conexant CX2584x Audio/Video decoder chip and related cores - * - * Integrated Consumer Infrared Controller - * - * Copyright (C) 2010 Andy Walls - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -#include -#include -#include -#include -#include - -#include "cx25840-core.h" - -static unsigned int ir_debug; -module_param(ir_debug, int, 0644); -MODULE_PARM_DESC(ir_debug, "enable integrated IR debug messages"); - -#define CX25840_IR_REG_BASE 0x200 - -#define CX25840_IR_CNTRL_REG 0x200 -#define CNTRL_WIN_3_3 0x00000000 -#define CNTRL_WIN_4_3 0x00000001 -#define CNTRL_WIN_3_4 0x00000002 -#define CNTRL_WIN_4_4 0x00000003 -#define CNTRL_WIN 0x00000003 -#define CNTRL_EDG_NONE 0x00000000 -#define CNTRL_EDG_FALL 0x00000004 -#define CNTRL_EDG_RISE 0x00000008 -#define CNTRL_EDG_BOTH 0x0000000C -#define CNTRL_EDG 0x0000000C -#define CNTRL_DMD 0x00000010 -#define CNTRL_MOD 0x00000020 -#define CNTRL_RFE 0x00000040 -#define CNTRL_TFE 0x00000080 -#define CNTRL_RXE 0x00000100 -#define CNTRL_TXE 0x00000200 -#define CNTRL_RIC 0x00000400 -#define CNTRL_TIC 0x00000800 -#define CNTRL_CPL 0x00001000 -#define CNTRL_LBM 0x00002000 -#define CNTRL_R 0x00004000 - -#define CX25840_IR_TXCLK_REG 0x204 -#define TXCLK_TCD 0x0000FFFF - -#define CX25840_IR_RXCLK_REG 0x208 -#define RXCLK_RCD 0x0000FFFF - -#define CX25840_IR_CDUTY_REG 0x20C -#define CDUTY_CDC 0x0000000F - -#define CX25840_IR_STATS_REG 0x210 -#define STATS_RTO 0x00000001 -#define STATS_ROR 0x00000002 -#define STATS_RBY 0x00000004 -#define STATS_TBY 0x00000008 -#define STATS_RSR 0x00000010 -#define STATS_TSR 0x00000020 - -#define CX25840_IR_IRQEN_REG 0x214 -#define IRQEN_RTE 0x00000001 -#define IRQEN_ROE 0x00000002 -#define IRQEN_RSE 0x00000010 -#define IRQEN_TSE 0x00000020 -#define IRQEN_MSK 0x00000033 - -#define CX25840_IR_FILTR_REG 0x218 -#define FILTR_LPF 0x0000FFFF - -#define CX25840_IR_FIFO_REG 0x23C -#define FIFO_RXTX 0x0000FFFF -#define FIFO_RXTX_LVL 0x00010000 -#define FIFO_RXTX_RTO 0x0001FFFF -#define FIFO_RX_NDV 0x00020000 -#define FIFO_RX_DEPTH 8 -#define FIFO_TX_DEPTH 8 - -#define CX25840_VIDCLK_FREQ 108000000 /* 108 MHz, BT.656 */ -#define CX25840_IR_REFCLK_FREQ (CX25840_VIDCLK_FREQ / 2) - -/* - * We use this union internally for convenience, but callers to tx_write - * and rx_read will be expecting records of type struct ir_raw_event. - * Always ensure the size of this union is dictated by struct ir_raw_event. - */ -union cx25840_ir_fifo_rec { - u32 hw_fifo_data; - struct ir_raw_event ir_core_data; -}; - -#define CX25840_IR_RX_KFIFO_SIZE (256 * sizeof(union cx25840_ir_fifo_rec)) -#define CX25840_IR_TX_KFIFO_SIZE (256 * sizeof(union cx25840_ir_fifo_rec)) - -struct cx25840_ir_state { - struct i2c_client *c; - - struct v4l2_subdev_ir_parameters rx_params; - struct mutex rx_params_lock; /* protects Rx parameter settings cache */ - atomic_t rxclk_divider; - atomic_t rx_invert; - - struct kfifo rx_kfifo; - spinlock_t rx_kfifo_lock; /* protect Rx data kfifo */ - - struct v4l2_subdev_ir_parameters tx_params; - struct mutex tx_params_lock; /* protects Tx parameter settings cache */ - atomic_t txclk_divider; -}; - -static inline struct cx25840_ir_state *to_ir_state(struct v4l2_subdev *sd) -{ - struct cx25840_state *state = to_state(sd); - return state ? state->ir_state : NULL; -} - - -/* - * Rx and Tx Clock Divider register computations - * - * Note the largest clock divider value of 0xffff corresponds to: - * (0xffff + 1) * 1000 / 108/2 MHz = 1,213,629.629... ns - * which fits in 21 bits, so we'll use unsigned int for time arguments. - */ -static inline u16 count_to_clock_divider(unsigned int d) -{ - if (d > RXCLK_RCD + 1) - d = RXCLK_RCD; - else if (d < 2) - d = 1; - else - d--; - return (u16) d; -} - -static inline u16 ns_to_clock_divider(unsigned int ns) -{ - return count_to_clock_divider( - DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ / 1000000 * ns, 1000)); -} - -static inline unsigned int clock_divider_to_ns(unsigned int divider) -{ - /* Period of the Rx or Tx clock in ns */ - return DIV_ROUND_CLOSEST((divider + 1) * 1000, - CX25840_IR_REFCLK_FREQ / 1000000); -} - -static inline u16 carrier_freq_to_clock_divider(unsigned int freq) -{ - return count_to_clock_divider( - DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, freq * 16)); -} - -static inline unsigned int clock_divider_to_carrier_freq(unsigned int divider) -{ - return DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, (divider + 1) * 16); -} - -static inline u16 freq_to_clock_divider(unsigned int freq, - unsigned int rollovers) -{ - return count_to_clock_divider( - DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, freq * rollovers)); -} - -static inline unsigned int clock_divider_to_freq(unsigned int divider, - unsigned int rollovers) -{ - return DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, - (divider + 1) * rollovers); -} - -/* - * Low Pass Filter register calculations - * - * Note the largest count value of 0xffff corresponds to: - * 0xffff * 1000 / 108/2 MHz = 1,213,611.11... ns - * which fits in 21 bits, so we'll use unsigned int for time arguments. - */ -static inline u16 count_to_lpf_count(unsigned int d) -{ - if (d > FILTR_LPF) - d = FILTR_LPF; - else if (d < 4) - d = 0; - return (u16) d; -} - -static inline u16 ns_to_lpf_count(unsigned int ns) -{ - return count_to_lpf_count( - DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ / 1000000 * ns, 1000)); -} - -static inline unsigned int lpf_count_to_ns(unsigned int count) -{ - /* Duration of the Low Pass Filter rejection window in ns */ - return DIV_ROUND_CLOSEST(count * 1000, - CX25840_IR_REFCLK_FREQ / 1000000); -} - -static inline unsigned int lpf_count_to_us(unsigned int count) -{ - /* Duration of the Low Pass Filter rejection window in us */ - return DIV_ROUND_CLOSEST(count, CX25840_IR_REFCLK_FREQ / 1000000); -} - -/* - * FIFO register pulse width count compuations - */ -static u32 clock_divider_to_resolution(u16 divider) -{ - /* - * Resolution is the duration of 1 tick of the readable portion of - * of the pulse width counter as read from the FIFO. The two lsb's are - * not readable, hence the << 2. This function returns ns. - */ - return DIV_ROUND_CLOSEST((1 << 2) * ((u32) divider + 1) * 1000, - CX25840_IR_REFCLK_FREQ / 1000000); -} - -static u64 pulse_width_count_to_ns(u16 count, u16 divider) -{ - u64 n; - u32 rem; - - /* - * The 2 lsb's of the pulse width timer count are not readable, hence - * the (count << 2) | 0x3 - */ - n = (((u64) count << 2) | 0x3) * (divider + 1) * 1000; /* millicycles */ - rem = do_div(n, CX25840_IR_REFCLK_FREQ / 1000000); /* / MHz => ns */ - if (rem >= CX25840_IR_REFCLK_FREQ / 1000000 / 2) - n++; - return n; -} - -#if 0 -/* Keep as we will need this for Transmit functionality */ -static u16 ns_to_pulse_width_count(u32 ns, u16 divider) -{ - u64 n; - u32 d; - u32 rem; - - /* - * The 2 lsb's of the pulse width timer count are not accessible, hence - * the (1 << 2) - */ - n = ((u64) ns) * CX25840_IR_REFCLK_FREQ / 1000000; /* millicycles */ - d = (1 << 2) * ((u32) divider + 1) * 1000; /* millicycles/count */ - rem = do_div(n, d); - if (rem >= d / 2) - n++; - - if (n > FIFO_RXTX) - n = FIFO_RXTX; - else if (n == 0) - n = 1; - return (u16) n; -} - -#endif -static unsigned int pulse_width_count_to_us(u16 count, u16 divider) -{ - u64 n; - u32 rem; - - /* - * The 2 lsb's of the pulse width timer count are not readable, hence - * the (count << 2) | 0x3 - */ - n = (((u64) count << 2) | 0x3) * (divider + 1); /* cycles */ - rem = do_div(n, CX25840_IR_REFCLK_FREQ / 1000000); /* / MHz => us */ - if (rem >= CX25840_IR_REFCLK_FREQ / 1000000 / 2) - n++; - return (unsigned int) n; -} - -/* - * Pulse Clocks computations: Combined Pulse Width Count & Rx Clock Counts - * - * The total pulse clock count is an 18 bit pulse width timer count as the most - * significant part and (up to) 16 bit clock divider count as a modulus. - * When the Rx clock divider ticks down to 0, it increments the 18 bit pulse - * width timer count's least significant bit. - */ -static u64 ns_to_pulse_clocks(u32 ns) -{ - u64 clocks; - u32 rem; - clocks = CX25840_IR_REFCLK_FREQ / 1000000 * (u64) ns; /* millicycles */ - rem = do_div(clocks, 1000); /* /1000 = cycles */ - if (rem >= 1000 / 2) - clocks++; - return clocks; -} - -static u16 pulse_clocks_to_clock_divider(u64 count) -{ - do_div(count, (FIFO_RXTX << 2) | 0x3); - - /* net result needs to be rounded down and decremented by 1 */ - if (count > RXCLK_RCD + 1) - count = RXCLK_RCD; - else if (count < 2) - count = 1; - else - count--; - return (u16) count; -} - -/* - * IR Control Register helpers - */ -enum tx_fifo_watermark { - TX_FIFO_HALF_EMPTY = 0, - TX_FIFO_EMPTY = CNTRL_TIC, -}; - -enum rx_fifo_watermark { - RX_FIFO_HALF_FULL = 0, - RX_FIFO_NOT_EMPTY = CNTRL_RIC, -}; - -static inline void control_tx_irq_watermark(struct i2c_client *c, - enum tx_fifo_watermark level) -{ - cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_TIC, level); -} - -static inline void control_rx_irq_watermark(struct i2c_client *c, - enum rx_fifo_watermark level) -{ - cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_RIC, level); -} - -static inline void control_tx_enable(struct i2c_client *c, bool enable) -{ - cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~(CNTRL_TXE | CNTRL_TFE), - enable ? (CNTRL_TXE | CNTRL_TFE) : 0); -} - -static inline void control_rx_enable(struct i2c_client *c, bool enable) -{ - cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~(CNTRL_RXE | CNTRL_RFE), - enable ? (CNTRL_RXE | CNTRL_RFE) : 0); -} - -static inline void control_tx_modulation_enable(struct i2c_client *c, - bool enable) -{ - cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_MOD, - enable ? CNTRL_MOD : 0); -} - -static inline void control_rx_demodulation_enable(struct i2c_client *c, - bool enable) -{ - cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_DMD, - enable ? CNTRL_DMD : 0); -} - -static inline void control_rx_s_edge_detection(struct i2c_client *c, - u32 edge_types) -{ - cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_EDG_BOTH, - edge_types & CNTRL_EDG_BOTH); -} - -static void control_rx_s_carrier_window(struct i2c_client *c, - unsigned int carrier, - unsigned int *carrier_range_low, - unsigned int *carrier_range_high) -{ - u32 v; - unsigned int c16 = carrier * 16; - - if (*carrier_range_low < DIV_ROUND_CLOSEST(c16, 16 + 3)) { - v = CNTRL_WIN_3_4; - *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 4); - } else { - v = CNTRL_WIN_3_3; - *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 3); - } - - if (*carrier_range_high > DIV_ROUND_CLOSEST(c16, 16 - 3)) { - v |= CNTRL_WIN_4_3; - *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 4); - } else { - v |= CNTRL_WIN_3_3; - *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 3); - } - cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_WIN, v); -} - -static inline void control_tx_polarity_invert(struct i2c_client *c, - bool invert) -{ - cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_CPL, - invert ? CNTRL_CPL : 0); -} - -/* - * IR Rx & Tx Clock Register helpers - */ -static unsigned int txclk_tx_s_carrier(struct i2c_client *c, - unsigned int freq, - u16 *divider) -{ - *divider = carrier_freq_to_clock_divider(freq); - cx25840_write4(c, CX25840_IR_TXCLK_REG, *divider); - return clock_divider_to_carrier_freq(*divider); -} - -static unsigned int rxclk_rx_s_carrier(struct i2c_client *c, - unsigned int freq, - u16 *divider) -{ - *divider = carrier_freq_to_clock_divider(freq); - cx25840_write4(c, CX25840_IR_RXCLK_REG, *divider); - return clock_divider_to_carrier_freq(*divider); -} - -static u32 txclk_tx_s_max_pulse_width(struct i2c_client *c, u32 ns, - u16 *divider) -{ - u64 pulse_clocks; - - if (ns > IR_MAX_DURATION) - ns = IR_MAX_DURATION; - pulse_clocks = ns_to_pulse_clocks(ns); - *divider = pulse_clocks_to_clock_divider(pulse_clocks); - cx25840_write4(c, CX25840_IR_TXCLK_REG, *divider); - return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider); -} - -static u32 rxclk_rx_s_max_pulse_width(struct i2c_client *c, u32 ns, - u16 *divider) -{ - u64 pulse_clocks; - - if (ns > IR_MAX_DURATION) - ns = IR_MAX_DURATION; - pulse_clocks = ns_to_pulse_clocks(ns); - *divider = pulse_clocks_to_clock_divider(pulse_clocks); - cx25840_write4(c, CX25840_IR_RXCLK_REG, *divider); - return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider); -} - -/* - * IR Tx Carrier Duty Cycle register helpers - */ -static unsigned int cduty_tx_s_duty_cycle(struct i2c_client *c, - unsigned int duty_cycle) -{ - u32 n; - n = DIV_ROUND_CLOSEST(duty_cycle * 100, 625); /* 16ths of 100% */ - if (n != 0) - n--; - if (n > 15) - n = 15; - cx25840_write4(c, CX25840_IR_CDUTY_REG, n); - return DIV_ROUND_CLOSEST((n + 1) * 100, 16); -} - -/* - * IR Filter Register helpers - */ -static u32 filter_rx_s_min_width(struct i2c_client *c, u32 min_width_ns) -{ - u32 count = ns_to_lpf_count(min_width_ns); - cx25840_write4(c, CX25840_IR_FILTR_REG, count); - return lpf_count_to_ns(count); -} - -/* - * IR IRQ Enable Register helpers - */ -static inline void irqenable_rx(struct v4l2_subdev *sd, u32 mask) -{ - struct cx25840_state *state = to_state(sd); - - if (is_cx23885(state) || is_cx23887(state)) - mask ^= IRQEN_MSK; - mask &= (IRQEN_RTE | IRQEN_ROE | IRQEN_RSE); - cx25840_and_or4(state->c, CX25840_IR_IRQEN_REG, - ~(IRQEN_RTE | IRQEN_ROE | IRQEN_RSE), mask); -} - -static inline void irqenable_tx(struct v4l2_subdev *sd, u32 mask) -{ - struct cx25840_state *state = to_state(sd); - - if (is_cx23885(state) || is_cx23887(state)) - mask ^= IRQEN_MSK; - mask &= IRQEN_TSE; - cx25840_and_or4(state->c, CX25840_IR_IRQEN_REG, ~IRQEN_TSE, mask); -} - -/* - * V4L2 Subdevice IR Ops - */ -int cx25840_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled) -{ - struct cx25840_state *state = to_state(sd); - struct cx25840_ir_state *ir_state = to_ir_state(sd); - struct i2c_client *c = NULL; - unsigned long flags; - - union cx25840_ir_fifo_rec rx_data[FIFO_RX_DEPTH]; - unsigned int i, j, k; - u32 events, v; - int tsr, rsr, rto, ror, tse, rse, rte, roe, kror; - u32 cntrl, irqen, stats; - - *handled = false; - if (ir_state == NULL) - return -ENODEV; - - c = ir_state->c; - - /* Only support the IR controller for the CX2388[57] AV Core for now */ - if (!(is_cx23885(state) || is_cx23887(state))) - return -ENODEV; - - cntrl = cx25840_read4(c, CX25840_IR_CNTRL_REG); - irqen = cx25840_read4(c, CX25840_IR_IRQEN_REG); - if (is_cx23885(state) || is_cx23887(state)) - irqen ^= IRQEN_MSK; - stats = cx25840_read4(c, CX25840_IR_STATS_REG); - - tsr = stats & STATS_TSR; /* Tx FIFO Service Request */ - rsr = stats & STATS_RSR; /* Rx FIFO Service Request */ - rto = stats & STATS_RTO; /* Rx Pulse Width Timer Time Out */ - ror = stats & STATS_ROR; /* Rx FIFO Over Run */ - - tse = irqen & IRQEN_TSE; /* Tx FIFO Service Request IRQ Enable */ - rse = irqen & IRQEN_RSE; /* Rx FIFO Service Reuqest IRQ Enable */ - rte = irqen & IRQEN_RTE; /* Rx Pulse Width Timer Time Out IRQ Enable */ - roe = irqen & IRQEN_ROE; /* Rx FIFO Over Run IRQ Enable */ - - v4l2_dbg(2, ir_debug, sd, "IR IRQ Status: %s %s %s %s %s %s\n", - tsr ? "tsr" : " ", rsr ? "rsr" : " ", - rto ? "rto" : " ", ror ? "ror" : " ", - stats & STATS_TBY ? "tby" : " ", - stats & STATS_RBY ? "rby" : " "); - - v4l2_dbg(2, ir_debug, sd, "IR IRQ Enables: %s %s %s %s\n", - tse ? "tse" : " ", rse ? "rse" : " ", - rte ? "rte" : " ", roe ? "roe" : " "); - - /* - * Transmitter interrupt service - */ - if (tse && tsr) { - /* - * TODO: - * Check the watermark threshold setting - * Pull FIFO_TX_DEPTH or FIFO_TX_DEPTH/2 entries from tx_kfifo - * Push the data to the hardware FIFO. - * If there was nothing more to send in the tx_kfifo, disable - * the TSR IRQ and notify the v4l2_device. - * If there was something in the tx_kfifo, check the tx_kfifo - * level and notify the v4l2_device, if it is low. - */ - /* For now, inhibit TSR interrupt until Tx is implemented */ - irqenable_tx(sd, 0); - events = V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ; - v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_TX_NOTIFY, &events); - *handled = true; - } - - /* - * Receiver interrupt service - */ - kror = 0; - if ((rse && rsr) || (rte && rto)) { - /* - * Receive data on RSR to clear the STATS_RSR. - * Receive data on RTO, since we may not have yet hit the RSR - * watermark when we receive the RTO. - */ - for (i = 0, v = FIFO_RX_NDV; - (v & FIFO_RX_NDV) && !kror; i = 0) { - for (j = 0; - (v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) { - v = cx25840_read4(c, CX25840_IR_FIFO_REG); - rx_data[i].hw_fifo_data = v & ~FIFO_RX_NDV; - i++; - } - if (i == 0) - break; - j = i * sizeof(union cx25840_ir_fifo_rec); - k = kfifo_in_locked(&ir_state->rx_kfifo, - (unsigned char *) rx_data, j, - &ir_state->rx_kfifo_lock); - if (k != j) - kror++; /* rx_kfifo over run */ - } - *handled = true; - } - - events = 0; - v = 0; - if (kror) { - events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN; - v4l2_err(sd, "IR receiver software FIFO overrun\n"); - } - if (roe && ror) { - /* - * The RX FIFO Enable (CNTRL_RFE) must be toggled to clear - * the Rx FIFO Over Run status (STATS_ROR) - */ - v |= CNTRL_RFE; - events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN; - v4l2_err(sd, "IR receiver hardware FIFO overrun\n"); - } - if (rte && rto) { - /* - * The IR Receiver Enable (CNTRL_RXE) must be toggled to clear - * the Rx Pulse Width Timer Time Out (STATS_RTO) - */ - v |= CNTRL_RXE; - events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED; - } - if (v) { - /* Clear STATS_ROR & STATS_RTO as needed by reseting hardware */ - cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl & ~v); - cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl); - *handled = true; - } - spin_lock_irqsave(&ir_state->rx_kfifo_lock, flags); - if (kfifo_len(&ir_state->rx_kfifo) >= CX25840_IR_RX_KFIFO_SIZE / 2) - events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ; - spin_unlock_irqrestore(&ir_state->rx_kfifo_lock, flags); - - if (events) - v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_RX_NOTIFY, &events); - return 0; -} - -/* Receiver */ -static int cx25840_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count, - ssize_t *num) -{ - struct cx25840_ir_state *ir_state = to_ir_state(sd); - bool invert; - u16 divider; - unsigned int i, n; - union cx25840_ir_fifo_rec *p; - unsigned u, v, w; - - if (ir_state == NULL) - return -ENODEV; - - invert = (bool) atomic_read(&ir_state->rx_invert); - divider = (u16) atomic_read(&ir_state->rxclk_divider); - - n = count / sizeof(union cx25840_ir_fifo_rec) - * sizeof(union cx25840_ir_fifo_rec); - if (n == 0) { - *num = 0; - return 0; - } - - n = kfifo_out_locked(&ir_state->rx_kfifo, buf, n, - &ir_state->rx_kfifo_lock); - - n /= sizeof(union cx25840_ir_fifo_rec); - *num = n * sizeof(union cx25840_ir_fifo_rec); - - for (p = (union cx25840_ir_fifo_rec *) buf, i = 0; i < n; p++, i++) { - - if ((p->hw_fifo_data & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) { - /* Assume RTO was because of no IR light input */ - u = 0; - w = 1; - } else { - u = (p->hw_fifo_data & FIFO_RXTX_LVL) ? 1 : 0; - if (invert) - u = u ? 0 : 1; - w = 0; - } - - v = (unsigned) pulse_width_count_to_ns( - (u16) (p->hw_fifo_data & FIFO_RXTX), divider); - if (v > IR_MAX_DURATION) - v = IR_MAX_DURATION; - - init_ir_raw_event(&p->ir_core_data); - p->ir_core_data.pulse = u; - p->ir_core_data.duration = v; - p->ir_core_data.timeout = w; - - v4l2_dbg(2, ir_debug, sd, "rx read: %10u ns %s %s\n", - v, u ? "mark" : "space", w ? "(timed out)" : ""); - if (w) - v4l2_dbg(2, ir_debug, sd, "rx read: end of rx\n"); - } - return 0; -} - -static int cx25840_ir_rx_g_parameters(struct v4l2_subdev *sd, - struct v4l2_subdev_ir_parameters *p) -{ - struct cx25840_ir_state *ir_state = to_ir_state(sd); - - if (ir_state == NULL) - return -ENODEV; - - mutex_lock(&ir_state->rx_params_lock); - memcpy(p, &ir_state->rx_params, - sizeof(struct v4l2_subdev_ir_parameters)); - mutex_unlock(&ir_state->rx_params_lock); - return 0; -} - -static int cx25840_ir_rx_shutdown(struct v4l2_subdev *sd) -{ - struct cx25840_ir_state *ir_state = to_ir_state(sd); - struct i2c_client *c; - - if (ir_state == NULL) - return -ENODEV; - - c = ir_state->c; - mutex_lock(&ir_state->rx_params_lock); - - /* Disable or slow down all IR Rx circuits and counters */ - irqenable_rx(sd, 0); - control_rx_enable(c, false); - control_rx_demodulation_enable(c, false); - control_rx_s_edge_detection(c, CNTRL_EDG_NONE); - filter_rx_s_min_width(c, 0); - cx25840_write4(c, CX25840_IR_RXCLK_REG, RXCLK_RCD); - - ir_state->rx_params.shutdown = true; - - mutex_unlock(&ir_state->rx_params_lock); - return 0; -} - -static int cx25840_ir_rx_s_parameters(struct v4l2_subdev *sd, - struct v4l2_subdev_ir_parameters *p) -{ - struct cx25840_ir_state *ir_state = to_ir_state(sd); - struct i2c_client *c; - struct v4l2_subdev_ir_parameters *o; - u16 rxclk_divider; - - if (ir_state == NULL) - return -ENODEV; - - if (p->shutdown) - return cx25840_ir_rx_shutdown(sd); - - if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH) - return -ENOSYS; - - c = ir_state->c; - o = &ir_state->rx_params; - - mutex_lock(&ir_state->rx_params_lock); - - o->shutdown = p->shutdown; - - p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; - o->mode = p->mode; - - p->bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec); - o->bytes_per_data_element = p->bytes_per_data_element; - - /* Before we tweak the hardware, we have to disable the receiver */ - irqenable_rx(sd, 0); - control_rx_enable(c, false); - - control_rx_demodulation_enable(c, p->modulation); - o->modulation = p->modulation; - - if (p->modulation) { - p->carrier_freq = rxclk_rx_s_carrier(c, p->carrier_freq, - &rxclk_divider); - - o->carrier_freq = p->carrier_freq; - - p->duty_cycle = 50; - o->duty_cycle = p->duty_cycle; - - control_rx_s_carrier_window(c, p->carrier_freq, - &p->carrier_range_lower, - &p->carrier_range_upper); - o->carrier_range_lower = p->carrier_range_lower; - o->carrier_range_upper = p->carrier_range_upper; - - p->max_pulse_width = - (u32) pulse_width_count_to_ns(FIFO_RXTX, rxclk_divider); - } else { - p->max_pulse_width = - rxclk_rx_s_max_pulse_width(c, p->max_pulse_width, - &rxclk_divider); - } - o->max_pulse_width = p->max_pulse_width; - atomic_set(&ir_state->rxclk_divider, rxclk_divider); - - p->noise_filter_min_width = - filter_rx_s_min_width(c, p->noise_filter_min_width); - o->noise_filter_min_width = p->noise_filter_min_width; - - p->resolution = clock_divider_to_resolution(rxclk_divider); - o->resolution = p->resolution; - - /* FIXME - make this dependent on resolution for better performance */ - control_rx_irq_watermark(c, RX_FIFO_HALF_FULL); - - control_rx_s_edge_detection(c, CNTRL_EDG_BOTH); - - o->invert_level = p->invert_level; - atomic_set(&ir_state->rx_invert, p->invert_level); - - o->interrupt_enable = p->interrupt_enable; - o->enable = p->enable; - if (p->enable) { - unsigned long flags; - - spin_lock_irqsave(&ir_state->rx_kfifo_lock, flags); - kfifo_reset(&ir_state->rx_kfifo); - spin_unlock_irqrestore(&ir_state->rx_kfifo_lock, flags); - if (p->interrupt_enable) - irqenable_rx(sd, IRQEN_RSE | IRQEN_RTE | IRQEN_ROE); - control_rx_enable(c, p->enable); - } - - mutex_unlock(&ir_state->rx_params_lock); - return 0; -} - -/* Transmitter */ -static int cx25840_ir_tx_write(struct v4l2_subdev *sd, u8 *buf, size_t count, - ssize_t *num) -{ - struct cx25840_ir_state *ir_state = to_ir_state(sd); - - if (ir_state == NULL) - return -ENODEV; - -#if 0 - /* - * FIXME - the code below is an incomplete and untested sketch of what - * may need to be done. The critical part is to get 4 (or 8) pulses - * from the tx_kfifo, or converted from ns to the proper units from the - * input, and push them off to the hardware Tx FIFO right away, if the - * HW TX fifo needs service. The rest can be pushed to the tx_kfifo in - * a less critical timeframe. Also watch out for overruning the - * tx_kfifo - don't let it happen and let the caller know not all his - * pulses were written. - */ - u32 *ns_pulse = (u32 *) buf; - unsigned int n; - u32 fifo_pulse[FIFO_TX_DEPTH]; - u32 mark; - - /* Compute how much we can fit in the tx kfifo */ - n = CX25840_IR_TX_KFIFO_SIZE - kfifo_len(ir_state->tx_kfifo); - n = min(n, (unsigned int) count); - n /= sizeof(u32); - - /* FIXME - turn on Tx Fifo service interrupt - * check hardware fifo level, and other stuff - */ - for (i = 0; i < n; ) { - for (j = 0; j < FIFO_TX_DEPTH / 2 && i < n; j++) { - mark = ns_pulse[i] & LEVEL_MASK; - fifo_pulse[j] = ns_to_pulse_width_count( - ns_pulse[i] & - ~LEVEL_MASK, - ir_state->txclk_divider); - if (mark) - fifo_pulse[j] &= FIFO_RXTX_LVL; - i++; - } - kfifo_put(ir_state->tx_kfifo, (u8 *) fifo_pulse, - j * sizeof(u32)); - } - *num = n * sizeof(u32); -#else - /* For now enable the Tx FIFO Service interrupt & pretend we did work */ - irqenable_tx(sd, IRQEN_TSE); - *num = count; -#endif - return 0; -} - -static int cx25840_ir_tx_g_parameters(struct v4l2_subdev *sd, - struct v4l2_subdev_ir_parameters *p) -{ - struct cx25840_ir_state *ir_state = to_ir_state(sd); - - if (ir_state == NULL) - return -ENODEV; - - mutex_lock(&ir_state->tx_params_lock); - memcpy(p, &ir_state->tx_params, - sizeof(struct v4l2_subdev_ir_parameters)); - mutex_unlock(&ir_state->tx_params_lock); - return 0; -} - -static int cx25840_ir_tx_shutdown(struct v4l2_subdev *sd) -{ - struct cx25840_ir_state *ir_state = to_ir_state(sd); - struct i2c_client *c; - - if (ir_state == NULL) - return -ENODEV; - - c = ir_state->c; - mutex_lock(&ir_state->tx_params_lock); - - /* Disable or slow down all IR Tx circuits and counters */ - irqenable_tx(sd, 0); - control_tx_enable(c, false); - control_tx_modulation_enable(c, false); - cx25840_write4(c, CX25840_IR_TXCLK_REG, TXCLK_TCD); - - ir_state->tx_params.shutdown = true; - - mutex_unlock(&ir_state->tx_params_lock); - return 0; -} - -static int cx25840_ir_tx_s_parameters(struct v4l2_subdev *sd, - struct v4l2_subdev_ir_parameters *p) -{ - struct cx25840_ir_state *ir_state = to_ir_state(sd); - struct i2c_client *c; - struct v4l2_subdev_ir_parameters *o; - u16 txclk_divider; - - if (ir_state == NULL) - return -ENODEV; - - if (p->shutdown) - return cx25840_ir_tx_shutdown(sd); - - if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH) - return -ENOSYS; - - c = ir_state->c; - o = &ir_state->tx_params; - mutex_lock(&ir_state->tx_params_lock); - - o->shutdown = p->shutdown; - - p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; - o->mode = p->mode; - - p->bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec); - o->bytes_per_data_element = p->bytes_per_data_element; - - /* Before we tweak the hardware, we have to disable the transmitter */ - irqenable_tx(sd, 0); - control_tx_enable(c, false); - - control_tx_modulation_enable(c, p->modulation); - o->modulation = p->modulation; - - if (p->modulation) { - p->carrier_freq = txclk_tx_s_carrier(c, p->carrier_freq, - &txclk_divider); - o->carrier_freq = p->carrier_freq; - - p->duty_cycle = cduty_tx_s_duty_cycle(c, p->duty_cycle); - o->duty_cycle = p->duty_cycle; - - p->max_pulse_width = - (u32) pulse_width_count_to_ns(FIFO_RXTX, txclk_divider); - } else { - p->max_pulse_width = - txclk_tx_s_max_pulse_width(c, p->max_pulse_width, - &txclk_divider); - } - o->max_pulse_width = p->max_pulse_width; - atomic_set(&ir_state->txclk_divider, txclk_divider); - - p->resolution = clock_divider_to_resolution(txclk_divider); - o->resolution = p->resolution; - - /* FIXME - make this dependent on resolution for better performance */ - control_tx_irq_watermark(c, TX_FIFO_HALF_EMPTY); - - control_tx_polarity_invert(c, p->invert_carrier_sense); - o->invert_carrier_sense = p->invert_carrier_sense; - - /* - * FIXME: we don't have hardware help for IO pin level inversion - * here like we have on the CX23888. - * Act on this with some mix of logical inversion of data levels, - * carrier polarity, and carrier duty cycle. - */ - o->invert_level = p->invert_level; - - o->interrupt_enable = p->interrupt_enable; - o->enable = p->enable; - if (p->enable) { - /* reset tx_fifo here */ - if (p->interrupt_enable) - irqenable_tx(sd, IRQEN_TSE); - control_tx_enable(c, p->enable); - } - - mutex_unlock(&ir_state->tx_params_lock); - return 0; -} - - -/* - * V4L2 Subdevice Core Ops support - */ -int cx25840_ir_log_status(struct v4l2_subdev *sd) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *c = state->c; - char *s; - int i, j; - u32 cntrl, txclk, rxclk, cduty, stats, irqen, filtr; - - /* The CX23888 chip doesn't have an IR controller on the A/V core */ - if (is_cx23888(state)) - return 0; - - cntrl = cx25840_read4(c, CX25840_IR_CNTRL_REG); - txclk = cx25840_read4(c, CX25840_IR_TXCLK_REG) & TXCLK_TCD; - rxclk = cx25840_read4(c, CX25840_IR_RXCLK_REG) & RXCLK_RCD; - cduty = cx25840_read4(c, CX25840_IR_CDUTY_REG) & CDUTY_CDC; - stats = cx25840_read4(c, CX25840_IR_STATS_REG); - irqen = cx25840_read4(c, CX25840_IR_IRQEN_REG); - if (is_cx23885(state) || is_cx23887(state)) - irqen ^= IRQEN_MSK; - filtr = cx25840_read4(c, CX25840_IR_FILTR_REG) & FILTR_LPF; - - v4l2_info(sd, "IR Receiver:\n"); - v4l2_info(sd, "\tEnabled: %s\n", - cntrl & CNTRL_RXE ? "yes" : "no"); - v4l2_info(sd, "\tDemodulation from a carrier: %s\n", - cntrl & CNTRL_DMD ? "enabled" : "disabled"); - v4l2_info(sd, "\tFIFO: %s\n", - cntrl & CNTRL_RFE ? "enabled" : "disabled"); - switch (cntrl & CNTRL_EDG) { - case CNTRL_EDG_NONE: - s = "disabled"; - break; - case CNTRL_EDG_FALL: - s = "falling edge"; - break; - case CNTRL_EDG_RISE: - s = "rising edge"; - break; - case CNTRL_EDG_BOTH: - s = "rising & falling edges"; - break; - default: - s = "??? edge"; - break; - } - v4l2_info(sd, "\tPulse timers' start/stop trigger: %s\n", s); - v4l2_info(sd, "\tFIFO data on pulse timer overflow: %s\n", - cntrl & CNTRL_R ? "not loaded" : "overflow marker"); - v4l2_info(sd, "\tFIFO interrupt watermark: %s\n", - cntrl & CNTRL_RIC ? "not empty" : "half full or greater"); - v4l2_info(sd, "\tLoopback mode: %s\n", - cntrl & CNTRL_LBM ? "loopback active" : "normal receive"); - if (cntrl & CNTRL_DMD) { - v4l2_info(sd, "\tExpected carrier (16 clocks): %u Hz\n", - clock_divider_to_carrier_freq(rxclk)); - switch (cntrl & CNTRL_WIN) { - case CNTRL_WIN_3_3: - i = 3; - j = 3; - break; - case CNTRL_WIN_4_3: - i = 4; - j = 3; - break; - case CNTRL_WIN_3_4: - i = 3; - j = 4; - break; - case CNTRL_WIN_4_4: - i = 4; - j = 4; - break; - default: - i = 0; - j = 0; - break; - } - v4l2_info(sd, "\tNext carrier edge window: 16 clocks " - "-%1d/+%1d, %u to %u Hz\n", i, j, - clock_divider_to_freq(rxclk, 16 + j), - clock_divider_to_freq(rxclk, 16 - i)); - } - v4l2_info(sd, "\tMax measurable pulse width: %u us, %llu ns\n", - pulse_width_count_to_us(FIFO_RXTX, rxclk), - pulse_width_count_to_ns(FIFO_RXTX, rxclk)); - v4l2_info(sd, "\tLow pass filter: %s\n", - filtr ? "enabled" : "disabled"); - if (filtr) - v4l2_info(sd, "\tMin acceptable pulse width (LPF): %u us, " - "%u ns\n", - lpf_count_to_us(filtr), - lpf_count_to_ns(filtr)); - v4l2_info(sd, "\tPulse width timer timed-out: %s\n", - stats & STATS_RTO ? "yes" : "no"); - v4l2_info(sd, "\tPulse width timer time-out intr: %s\n", - irqen & IRQEN_RTE ? "enabled" : "disabled"); - v4l2_info(sd, "\tFIFO overrun: %s\n", - stats & STATS_ROR ? "yes" : "no"); - v4l2_info(sd, "\tFIFO overrun interrupt: %s\n", - irqen & IRQEN_ROE ? "enabled" : "disabled"); - v4l2_info(sd, "\tBusy: %s\n", - stats & STATS_RBY ? "yes" : "no"); - v4l2_info(sd, "\tFIFO service requested: %s\n", - stats & STATS_RSR ? "yes" : "no"); - v4l2_info(sd, "\tFIFO service request interrupt: %s\n", - irqen & IRQEN_RSE ? "enabled" : "disabled"); - - v4l2_info(sd, "IR Transmitter:\n"); - v4l2_info(sd, "\tEnabled: %s\n", - cntrl & CNTRL_TXE ? "yes" : "no"); - v4l2_info(sd, "\tModulation onto a carrier: %s\n", - cntrl & CNTRL_MOD ? "enabled" : "disabled"); - v4l2_info(sd, "\tFIFO: %s\n", - cntrl & CNTRL_TFE ? "enabled" : "disabled"); - v4l2_info(sd, "\tFIFO interrupt watermark: %s\n", - cntrl & CNTRL_TIC ? "not empty" : "half full or less"); - v4l2_info(sd, "\tCarrier polarity: %s\n", - cntrl & CNTRL_CPL ? "space:burst mark:noburst" - : "space:noburst mark:burst"); - if (cntrl & CNTRL_MOD) { - v4l2_info(sd, "\tCarrier (16 clocks): %u Hz\n", - clock_divider_to_carrier_freq(txclk)); - v4l2_info(sd, "\tCarrier duty cycle: %2u/16\n", - cduty + 1); - } - v4l2_info(sd, "\tMax pulse width: %u us, %llu ns\n", - pulse_width_count_to_us(FIFO_RXTX, txclk), - pulse_width_count_to_ns(FIFO_RXTX, txclk)); - v4l2_info(sd, "\tBusy: %s\n", - stats & STATS_TBY ? "yes" : "no"); - v4l2_info(sd, "\tFIFO service requested: %s\n", - stats & STATS_TSR ? "yes" : "no"); - v4l2_info(sd, "\tFIFO service request interrupt: %s\n", - irqen & IRQEN_TSE ? "enabled" : "disabled"); - - return 0; -} - - -const struct v4l2_subdev_ir_ops cx25840_ir_ops = { - .rx_read = cx25840_ir_rx_read, - .rx_g_parameters = cx25840_ir_rx_g_parameters, - .rx_s_parameters = cx25840_ir_rx_s_parameters, - - .tx_write = cx25840_ir_tx_write, - .tx_g_parameters = cx25840_ir_tx_g_parameters, - .tx_s_parameters = cx25840_ir_tx_s_parameters, -}; - - -static const struct v4l2_subdev_ir_parameters default_rx_params = { - .bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec), - .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, - - .enable = false, - .interrupt_enable = false, - .shutdown = true, - - .modulation = true, - .carrier_freq = 36000, /* 36 kHz - RC-5, and RC-6 carrier */ - - /* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */ - /* RC-6: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */ - .noise_filter_min_width = 333333, /* ns */ - .carrier_range_lower = 35000, - .carrier_range_upper = 37000, - .invert_level = false, -}; - -static const struct v4l2_subdev_ir_parameters default_tx_params = { - .bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec), - .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, - - .enable = false, - .interrupt_enable = false, - .shutdown = true, - - .modulation = true, - .carrier_freq = 36000, /* 36 kHz - RC-5 carrier */ - .duty_cycle = 25, /* 25 % - RC-5 carrier */ - .invert_level = false, - .invert_carrier_sense = false, -}; - -int cx25840_ir_probe(struct v4l2_subdev *sd) -{ - struct cx25840_state *state = to_state(sd); - struct cx25840_ir_state *ir_state; - struct v4l2_subdev_ir_parameters default_params; - - /* Only init the IR controller for the CX2388[57] AV Core for now */ - if (!(is_cx23885(state) || is_cx23887(state))) - return 0; - - ir_state = kzalloc(sizeof(struct cx25840_ir_state), GFP_KERNEL); - if (ir_state == NULL) - return -ENOMEM; - - spin_lock_init(&ir_state->rx_kfifo_lock); - if (kfifo_alloc(&ir_state->rx_kfifo, - CX25840_IR_RX_KFIFO_SIZE, GFP_KERNEL)) { - kfree(ir_state); - return -ENOMEM; - } - - ir_state->c = state->c; - state->ir_state = ir_state; - - /* Ensure no interrupts arrive yet */ - if (is_cx23885(state) || is_cx23887(state)) - cx25840_write4(ir_state->c, CX25840_IR_IRQEN_REG, IRQEN_MSK); - else - cx25840_write4(ir_state->c, CX25840_IR_IRQEN_REG, 0); - - mutex_init(&ir_state->rx_params_lock); - memcpy(&default_params, &default_rx_params, - sizeof(struct v4l2_subdev_ir_parameters)); - v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params); - - mutex_init(&ir_state->tx_params_lock); - memcpy(&default_params, &default_tx_params, - sizeof(struct v4l2_subdev_ir_parameters)); - v4l2_subdev_call(sd, ir, tx_s_parameters, &default_params); - - return 0; -} - -int cx25840_ir_remove(struct v4l2_subdev *sd) -{ - struct cx25840_state *state = to_state(sd); - struct cx25840_ir_state *ir_state = to_ir_state(sd); - - if (ir_state == NULL) - return -ENODEV; - - cx25840_ir_rx_shutdown(sd); - cx25840_ir_tx_shutdown(sd); - - kfifo_free(&ir_state->rx_kfifo); - kfree(ir_state); - state->ir_state = NULL; - return 0; -} diff --git a/drivers/media/video/cx25840/cx25840-vbi.c b/drivers/media/video/cx25840/cx25840-vbi.c deleted file mode 100644 index 64a4004f8a9..00000000000 --- a/drivers/media/video/cx25840/cx25840-vbi.c +++ /dev/null @@ -1,256 +0,0 @@ -/* cx25840 VBI functions - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - - -#include -#include -#include -#include - -#include "cx25840-core.h" - -static int odd_parity(u8 c) -{ - c ^= (c >> 4); - c ^= (c >> 2); - c ^= (c >> 1); - - return c & 1; -} - -static int decode_vps(u8 * dst, u8 * p) -{ - static const u8 biphase_tbl[] = { - 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, - 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, - 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, - 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, - 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, - 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, - 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, - 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, - 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, - 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, - 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87, - 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3, - 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85, - 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1, - 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, - 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, - 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, - 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, - 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86, - 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2, - 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84, - 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0, - 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, - 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, - 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, - 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, - 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, - 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, - 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, - 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, - 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, - 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, - }; - - u8 c, err = 0; - int i; - - for (i = 0; i < 2 * 13; i += 2) { - err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]]; - c = (biphase_tbl[p[i + 1]] & 0xf) | - ((biphase_tbl[p[i]] & 0xf) << 4); - dst[i / 2] = c; - } - - return err & 0xf0; -} - -int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct cx25840_state *state = to_state(sd); - static const u16 lcr2vbi[] = { - 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ - 0, V4L2_SLICED_WSS_625, 0, /* 4 */ - V4L2_SLICED_CAPTION_525, /* 6 */ - 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */ - 0, 0, 0, 0 - }; - int is_pal = !(state->std & V4L2_STD_525_60); - int i; - - memset(svbi, 0, sizeof(*svbi)); - /* we're done if raw VBI is active */ - if ((cx25840_read(client, 0x404) & 0x10) == 0) - return 0; - - if (is_pal) { - for (i = 7; i <= 23; i++) { - u8 v = cx25840_read(client, 0x424 + i - 7); - - svbi->service_lines[0][i] = lcr2vbi[v >> 4]; - svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; - svbi->service_set |= svbi->service_lines[0][i] | - svbi->service_lines[1][i]; - } - } else { - for (i = 10; i <= 21; i++) { - u8 v = cx25840_read(client, 0x424 + i - 10); - - svbi->service_lines[0][i] = lcr2vbi[v >> 4]; - svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; - svbi->service_set |= svbi->service_lines[0][i] | - svbi->service_lines[1][i]; - } - } - return 0; -} - -int cx25840_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct cx25840_state *state = to_state(sd); - int is_pal = !(state->std & V4L2_STD_525_60); - int vbi_offset = is_pal ? 1 : 0; - - /* Setup standard */ - cx25840_std_setup(client); - - /* VBI Offset */ - cx25840_write(client, 0x47f, vbi_offset); - cx25840_write(client, 0x404, 0x2e); - return 0; -} - -int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct cx25840_state *state = to_state(sd); - int is_pal = !(state->std & V4L2_STD_525_60); - int vbi_offset = is_pal ? 1 : 0; - int i, x; - u8 lcr[24]; - - for (x = 0; x <= 23; x++) - lcr[x] = 0x00; - - /* Setup standard */ - cx25840_std_setup(client); - - /* Sliced VBI */ - cx25840_write(client, 0x404, 0x32); /* Ancillary data */ - cx25840_write(client, 0x406, 0x13); - cx25840_write(client, 0x47f, vbi_offset); - - if (is_pal) { - for (i = 0; i <= 6; i++) - svbi->service_lines[0][i] = - svbi->service_lines[1][i] = 0; - } else { - for (i = 0; i <= 9; i++) - svbi->service_lines[0][i] = - svbi->service_lines[1][i] = 0; - - for (i = 22; i <= 23; i++) - svbi->service_lines[0][i] = - svbi->service_lines[1][i] = 0; - } - - for (i = 7; i <= 23; i++) { - for (x = 0; x <= 1; x++) { - switch (svbi->service_lines[1-x][i]) { - case V4L2_SLICED_TELETEXT_B: - lcr[i] |= 1 << (4 * x); - break; - case V4L2_SLICED_WSS_625: - lcr[i] |= 4 << (4 * x); - break; - case V4L2_SLICED_CAPTION_525: - lcr[i] |= 6 << (4 * x); - break; - case V4L2_SLICED_VPS: - lcr[i] |= 9 << (4 * x); - break; - } - } - } - - if (is_pal) { - for (x = 1, i = 0x424; i <= 0x434; i++, x++) - cx25840_write(client, i, lcr[6 + x]); - } else { - for (x = 1, i = 0x424; i <= 0x430; i++, x++) - cx25840_write(client, i, lcr[9 + x]); - for (i = 0x431; i <= 0x434; i++) - cx25840_write(client, i, 0); - } - - cx25840_write(client, 0x43c, 0x16); - cx25840_write(client, 0x474, is_pal ? 0x2a : 0x22); - return 0; -} - -int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi) -{ - struct cx25840_state *state = to_state(sd); - u8 *p = vbi->p; - int id1, id2, l, err = 0; - - if (p[0] || p[1] != 0xff || p[2] != 0xff || - (p[3] != 0x55 && p[3] != 0x91)) { - vbi->line = vbi->type = 0; - return 0; - } - - p += 4; - id1 = p[-1]; - id2 = p[0] & 0xf; - l = p[2] & 0x3f; - l += state->vbi_line_offset; - p += 4; - - switch (id2) { - case 1: - id2 = V4L2_SLICED_TELETEXT_B; - break; - case 4: - id2 = V4L2_SLICED_WSS_625; - break; - case 6: - id2 = V4L2_SLICED_CAPTION_525; - err = !odd_parity(p[0]) || !odd_parity(p[1]); - break; - case 9: - id2 = V4L2_SLICED_VPS; - if (decode_vps(p, p) != 0) - err = 1; - break; - default: - id2 = 0; - err = 1; - break; - } - - vbi->type = err ? 0 : id2; - vbi->line = err ? 0 : l; - vbi->is_second_field = err ? 0 : (id1 == 0x55); - vbi->p = p; - return 0; -} diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c deleted file mode 100644 index 04f192a0398..00000000000 --- a/drivers/media/video/ir-kbd-i2c.c +++ /dev/null @@ -1,489 +0,0 @@ -/* - * - * keyboard input driver for i2c IR remote controls - * - * Copyright (c) 2000-2003 Gerd Knorr - * modified for PixelView (BT878P+W/FM) by - * Michal Kochanowicz - * Christoph Bartelmus - * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by - * Ulrich Mueller - * modified for em2820 based USB TV tuners by - * Markus Rechberger - * modified for DViCO Fusion HDTV 5 RT GOLD by - * Chaogui Zhang - * modified for MSI TV@nywhere Plus by - * Henry Wong - * Mark Schultz - * Brian Rogers - * modified for AVerMedia Cardbus by - * Oldrich Jedlicka - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -/* ----------------------------------------------------------------------- */ -/* insmod parameters */ - -static int debug; -module_param(debug, int, 0644); /* debug level (0,1,2) */ - - -#define MODULE_NAME "ir-kbd-i2c" -#define dprintk(level, fmt, arg...) if (debug >= level) \ - printk(KERN_DEBUG MODULE_NAME ": " fmt , ## arg) - -/* ----------------------------------------------------------------------- */ - -static int get_key_haup_common(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, - int size, int offset) -{ - unsigned char buf[6]; - int start, range, toggle, dev, code, ircode; - - /* poll IR chip */ - if (size != i2c_master_recv(ir->c, buf, size)) - return -EIO; - - /* split rc5 data block ... */ - start = (buf[offset] >> 7) & 1; - range = (buf[offset] >> 6) & 1; - toggle = (buf[offset] >> 5) & 1; - dev = buf[offset] & 0x1f; - code = (buf[offset+1] >> 2) & 0x3f; - - /* rc5 has two start bits - * the first bit must be one - * the second bit defines the command range (1 = 0-63, 0 = 64 - 127) - */ - if (!start) - /* no key pressed */ - return 0; - /* - * Hauppauge remotes (black/silver) always use - * specific device ids. If we do not filter the - * device ids then messages destined for devices - * such as TVs (id=0) will get through causing - * mis-fired events. - * - * We also filter out invalid key presses which - * produce annoying debug log entries. - */ - ircode= (start << 12) | (toggle << 11) | (dev << 6) | code; - if ((ircode & 0x1fff)==0x1fff) - /* invalid key press */ - return 0; - - if (!range) - code += 64; - - dprintk(1,"ir hauppauge (rc5): s%d r%d t%d dev=%d code=%d\n", - start, range, toggle, dev, code); - - /* return key */ - *ir_key = (dev << 8) | code; - *ir_raw = ircode; - return 1; -} - -static int get_key_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) -{ - return get_key_haup_common (ir, ir_key, ir_raw, 3, 0); -} - -static int get_key_haup_xvr(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) -{ - int ret; - unsigned char buf[1] = { 0 }; - - /* - * This is the same apparent "are you ready?" poll command observed - * watching Windows driver traffic and implemented in lirc_zilog. With - * this added, we get far saner remote behavior with z8 chips on usb - * connected devices, even with the default polling interval of 100ms. - */ - ret = i2c_master_send(ir->c, buf, 1); - if (ret != 1) - return (ret < 0) ? ret : -EINVAL; - - return get_key_haup_common (ir, ir_key, ir_raw, 6, 3); -} - -static int get_key_pixelview(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)) { - dprintk(1,"read error\n"); - return -EIO; - } - *ir_key = b; - *ir_raw = b; - return 1; -} - -static int get_key_fusionhdtv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) -{ - unsigned char buf[4]; - - /* poll IR chip */ - if (4 != i2c_master_recv(ir->c, buf, 4)) { - dprintk(1,"read error\n"); - return -EIO; - } - - if(buf[0] !=0 || buf[1] !=0 || buf[2] !=0 || buf[3] != 0) - dprintk(2, "%s: 0x%2x 0x%2x 0x%2x 0x%2x\n", __func__, - buf[0], buf[1], buf[2], buf[3]); - - /* no key pressed or signal from other ir remote */ - if(buf[0] != 0x1 || buf[1] != 0xfe) - return 0; - - *ir_key = buf[2]; - *ir_raw = (buf[2] << 8) | buf[3]; - - return 1; -} - -static int get_key_knc1(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)) { - dprintk(1,"read error\n"); - return -EIO; - } - - /* it seems that 0xFE indicates that a button is still hold - down, while 0xff indicates that no button is hold - down. 0xfe sequences are sometimes interrupted by 0xFF */ - - dprintk(2,"key %02x\n", b); - - if (b == 0xff) - return 0; - - if (b == 0xfe) - /* keep old data */ - return 1; - - *ir_key = b; - *ir_raw = b; - return 1; -} - -static int get_key_avermedia_cardbus(struct IR_i2c *ir, - u32 *ir_key, u32 *ir_raw) -{ - unsigned char subaddr, key, keygroup; - struct i2c_msg msg[] = { { .addr = ir->c->addr, .flags = 0, - .buf = &subaddr, .len = 1}, - { .addr = ir->c->addr, .flags = I2C_M_RD, - .buf = &key, .len = 1} }; - subaddr = 0x0d; - if (2 != i2c_transfer(ir->c->adapter, msg, 2)) { - dprintk(1, "read error\n"); - return -EIO; - } - - if (key == 0xff) - return 0; - - subaddr = 0x0b; - msg[1].buf = &keygroup; - if (2 != i2c_transfer(ir->c->adapter, msg, 2)) { - dprintk(1, "read error\n"); - return -EIO; - } - - if (keygroup == 0xff) - return 0; - - dprintk(1, "read key 0x%02x/0x%02x\n", key, keygroup); - if (keygroup < 2 || keygroup > 3) { - /* Only a warning */ - dprintk(1, "warning: invalid key group 0x%02x for key 0x%02x\n", - keygroup, key); - } - key |= (keygroup & 1) << 6; - - *ir_key = key; - *ir_raw = key; - return 1; -} - -/* ----------------------------------------------------------------------- */ - -static int ir_key_poll(struct IR_i2c *ir) -{ - static u32 ir_key, ir_raw; - int rc; - - dprintk(3, "%s\n", __func__); - rc = ir->get_key(ir, &ir_key, &ir_raw); - if (rc < 0) { - dprintk(2,"error\n"); - return rc; - } - - if (rc) { - dprintk(1, "%s: keycode = 0x%04x\n", __func__, ir_key); - rc_keydown(ir->rc, ir_key, 0); - } - return 0; -} - -static void ir_work(struct work_struct *work) -{ - int rc; - struct IR_i2c *ir = container_of(work, struct IR_i2c, work.work); - - rc = ir_key_poll(ir); - if (rc == -ENODEV) { - rc_unregister_device(ir->rc); - ir->rc = NULL; - return; - } - - schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling_interval)); -} - -/* ----------------------------------------------------------------------- */ - -static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ - char *ir_codes = NULL; - const char *name = NULL; - u64 rc_type = RC_TYPE_UNKNOWN; - struct IR_i2c *ir; - struct rc_dev *rc = NULL; - struct i2c_adapter *adap = client->adapter; - unsigned short addr = client->addr; - int err; - - ir = kzalloc(sizeof(struct IR_i2c), GFP_KERNEL); - if (!ir) - return -ENOMEM; - - ir->c = client; - ir->polling_interval = DEFAULT_POLLING_INTERVAL; - i2c_set_clientdata(client, ir); - - switch(addr) { - case 0x64: - name = "Pixelview"; - ir->get_key = get_key_pixelview; - rc_type = RC_TYPE_OTHER; - ir_codes = RC_MAP_EMPTY; - break; - case 0x18: - case 0x1f: - case 0x1a: - name = "Hauppauge"; - ir->get_key = get_key_haup; - rc_type = RC_TYPE_RC5; - ir_codes = RC_MAP_HAUPPAUGE; - break; - case 0x30: - name = "KNC One"; - ir->get_key = get_key_knc1; - rc_type = RC_TYPE_OTHER; - ir_codes = RC_MAP_EMPTY; - break; - case 0x6b: - name = "FusionHDTV"; - ir->get_key = get_key_fusionhdtv; - rc_type = RC_TYPE_RC5; - ir_codes = RC_MAP_FUSIONHDTV_MCE; - break; - case 0x40: - name = "AVerMedia Cardbus remote"; - ir->get_key = get_key_avermedia_cardbus; - rc_type = RC_TYPE_OTHER; - ir_codes = RC_MAP_AVERMEDIA_CARDBUS; - break; - case 0x71: - name = "Hauppauge/Zilog Z8"; - ir->get_key = get_key_haup_xvr; - rc_type = RC_TYPE_RC5; - ir_codes = RC_MAP_HAUPPAUGE; - break; - } - - /* Let the caller override settings */ - if (client->dev.platform_data) { - const struct IR_i2c_init_data *init_data = - client->dev.platform_data; - - ir_codes = init_data->ir_codes; - rc = init_data->rc_dev; - - name = init_data->name; - if (init_data->type) - rc_type = init_data->type; - - if (init_data->polling_interval) - ir->polling_interval = init_data->polling_interval; - - switch (init_data->internal_get_key_func) { - case IR_KBD_GET_KEY_CUSTOM: - /* The bridge driver provided us its own function */ - ir->get_key = init_data->get_key; - break; - case IR_KBD_GET_KEY_PIXELVIEW: - ir->get_key = get_key_pixelview; - break; - case IR_KBD_GET_KEY_HAUP: - ir->get_key = get_key_haup; - break; - case IR_KBD_GET_KEY_KNC1: - ir->get_key = get_key_knc1; - break; - case IR_KBD_GET_KEY_FUSIONHDTV: - ir->get_key = get_key_fusionhdtv; - break; - case IR_KBD_GET_KEY_HAUP_XVR: - ir->get_key = get_key_haup_xvr; - break; - case IR_KBD_GET_KEY_AVERMEDIA_CARDBUS: - ir->get_key = get_key_avermedia_cardbus; - break; - } - } - - if (!rc) { - /* - * If platform_data doesn't specify rc_dev, initilize it - * internally - */ - rc = rc_allocate_device(); - if (!rc) { - err = -ENOMEM; - goto err_out_free; - } - } - ir->rc = rc; - - /* Make sure we are all setup before going on */ - if (!name || !ir->get_key || !rc_type || !ir_codes) { - dprintk(1, ": Unsupported device at address 0x%02x\n", - addr); - err = -ENODEV; - goto err_out_free; - } - - /* Sets name */ - snprintf(ir->name, sizeof(ir->name), "i2c IR (%s)", name); - ir->ir_codes = ir_codes; - - snprintf(ir->phys, sizeof(ir->phys), "%s/%s/ir0", - dev_name(&adap->dev), - dev_name(&client->dev)); - - /* - * Initialize input_dev fields - * It doesn't make sense to allow overriding them via platform_data - */ - rc->input_id.bustype = BUS_I2C; - rc->input_phys = ir->phys; - rc->input_name = ir->name; - - /* - * Initialize the other fields of rc_dev - */ - rc->map_name = ir->ir_codes; - rc->allowed_protos = rc_type; - if (!rc->driver_name) - rc->driver_name = MODULE_NAME; - - err = rc_register_device(rc); - if (err) - goto err_out_free; - - printk(MODULE_NAME ": %s detected at %s [%s]\n", - ir->name, ir->phys, adap->name); - - /* start polling via eventd */ - INIT_DELAYED_WORK(&ir->work, ir_work); - schedule_delayed_work(&ir->work, 0); - - return 0; - - err_out_free: - /* Only frees rc if it were allocated internally */ - rc_free_device(rc); - kfree(ir); - return err; -} - -static int ir_remove(struct i2c_client *client) -{ - struct IR_i2c *ir = i2c_get_clientdata(client); - - /* kill outstanding polls */ - cancel_delayed_work_sync(&ir->work); - - /* unregister device */ - if (ir->rc) - rc_unregister_device(ir->rc); - - /* free memory */ - kfree(ir); - return 0; -} - -static const struct i2c_device_id ir_kbd_id[] = { - /* Generic entry for any IR receiver */ - { "ir_video", 0 }, - /* IR device specific entries should be added here */ - { "ir_rx_z8f0811_haup", 0 }, - { "ir_rx_z8f0811_hdpvr", 0 }, - { } -}; - -static struct i2c_driver ir_kbd_driver = { - .driver = { - .name = "ir-kbd-i2c", - }, - .probe = ir_probe, - .remove = ir_remove, - .id_table = ir_kbd_id, -}; - -module_i2c_driver(ir_kbd_driver); - -/* ----------------------------------------------------------------------- */ - -MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, Ulrich Mueller"); -MODULE_DESCRIPTION("input driver for i2c IR remote controls"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/ks0127.c b/drivers/media/video/ks0127.c deleted file mode 100644 index ee7ca2dcca2..00000000000 --- a/drivers/media/video/ks0127.c +++ /dev/null @@ -1,724 +0,0 @@ -/* - * Video Capture Driver (Video for Linux 1/2) - * for the Matrox Marvel G200,G400 and Rainbow Runner-G series - * - * This module is an interface to the KS0127 video decoder chip. - * - * Copyright (C) 1999 Ryan Drake - * - * 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. - * - ***************************************************************************** - * - * Modified and extended by - * Mike Bernson - * Gerard v.d. Horst - * Leon van Stuivenberg - * Gernot Ziegler - * - * Version History: - * V1.0 Ryan Drake Initial version by Ryan Drake - * V1.1 Gerard v.d. Horst Added some debugoutput, reset the video-standard - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ks0127.h" - -MODULE_DESCRIPTION("KS0127 video decoder driver"); -MODULE_AUTHOR("Ryan Drake"); -MODULE_LICENSE("GPL"); - -/* Addresses */ -#define I2C_KS0127_ADDON 0xD8 -#define I2C_KS0127_ONBOARD 0xDA - - -/* ks0127 control registers */ -#define KS_STAT 0x00 -#define KS_CMDA 0x01 -#define KS_CMDB 0x02 -#define KS_CMDC 0x03 -#define KS_CMDD 0x04 -#define KS_HAVB 0x05 -#define KS_HAVE 0x06 -#define KS_HS1B 0x07 -#define KS_HS1E 0x08 -#define KS_HS2B 0x09 -#define KS_HS2E 0x0a -#define KS_AGC 0x0b -#define KS_HXTRA 0x0c -#define KS_CDEM 0x0d -#define KS_PORTAB 0x0e -#define KS_LUMA 0x0f -#define KS_CON 0x10 -#define KS_BRT 0x11 -#define KS_CHROMA 0x12 -#define KS_CHROMB 0x13 -#define KS_DEMOD 0x14 -#define KS_SAT 0x15 -#define KS_HUE 0x16 -#define KS_VERTIA 0x17 -#define KS_VERTIB 0x18 -#define KS_VERTIC 0x19 -#define KS_HSCLL 0x1a -#define KS_HSCLH 0x1b -#define KS_VSCLL 0x1c -#define KS_VSCLH 0x1d -#define KS_OFMTA 0x1e -#define KS_OFMTB 0x1f -#define KS_VBICTL 0x20 -#define KS_CCDAT2 0x21 -#define KS_CCDAT1 0x22 -#define KS_VBIL30 0x23 -#define KS_VBIL74 0x24 -#define KS_VBIL118 0x25 -#define KS_VBIL1512 0x26 -#define KS_TTFRAM 0x27 -#define KS_TESTA 0x28 -#define KS_UVOFFH 0x29 -#define KS_UVOFFL 0x2a -#define KS_UGAIN 0x2b -#define KS_VGAIN 0x2c -#define KS_VAVB 0x2d -#define KS_VAVE 0x2e -#define KS_CTRACK 0x2f -#define KS_POLCTL 0x30 -#define KS_REFCOD 0x31 -#define KS_INVALY 0x32 -#define KS_INVALU 0x33 -#define KS_INVALV 0x34 -#define KS_UNUSEY 0x35 -#define KS_UNUSEU 0x36 -#define KS_UNUSEV 0x37 -#define KS_USRSAV 0x38 -#define KS_USREAV 0x39 -#define KS_SHS1A 0x3a -#define KS_SHS1B 0x3b -#define KS_SHS1C 0x3c -#define KS_CMDE 0x3d -#define KS_VSDEL 0x3e -#define KS_CMDF 0x3f -#define KS_GAMMA0 0x40 -#define KS_GAMMA1 0x41 -#define KS_GAMMA2 0x42 -#define KS_GAMMA3 0x43 -#define KS_GAMMA4 0x44 -#define KS_GAMMA5 0x45 -#define KS_GAMMA6 0x46 -#define KS_GAMMA7 0x47 -#define KS_GAMMA8 0x48 -#define KS_GAMMA9 0x49 -#define KS_GAMMA10 0x4a -#define KS_GAMMA11 0x4b -#define KS_GAMMA12 0x4c -#define KS_GAMMA13 0x4d -#define KS_GAMMA14 0x4e -#define KS_GAMMA15 0x4f -#define KS_GAMMA16 0x50 -#define KS_GAMMA17 0x51 -#define KS_GAMMA18 0x52 -#define KS_GAMMA19 0x53 -#define KS_GAMMA20 0x54 -#define KS_GAMMA21 0x55 -#define KS_GAMMA22 0x56 -#define KS_GAMMA23 0x57 -#define KS_GAMMA24 0x58 -#define KS_GAMMA25 0x59 -#define KS_GAMMA26 0x5a -#define KS_GAMMA27 0x5b -#define KS_GAMMA28 0x5c -#define KS_GAMMA29 0x5d -#define KS_GAMMA30 0x5e -#define KS_GAMMA31 0x5f -#define KS_GAMMAD0 0x60 -#define KS_GAMMAD1 0x61 -#define KS_GAMMAD2 0x62 -#define KS_GAMMAD3 0x63 -#define KS_GAMMAD4 0x64 -#define KS_GAMMAD5 0x65 -#define KS_GAMMAD6 0x66 -#define KS_GAMMAD7 0x67 -#define KS_GAMMAD8 0x68 -#define KS_GAMMAD9 0x69 -#define KS_GAMMAD10 0x6a -#define KS_GAMMAD11 0x6b -#define KS_GAMMAD12 0x6c -#define KS_GAMMAD13 0x6d -#define KS_GAMMAD14 0x6e -#define KS_GAMMAD15 0x6f -#define KS_GAMMAD16 0x70 -#define KS_GAMMAD17 0x71 -#define KS_GAMMAD18 0x72 -#define KS_GAMMAD19 0x73 -#define KS_GAMMAD20 0x74 -#define KS_GAMMAD21 0x75 -#define KS_GAMMAD22 0x76 -#define KS_GAMMAD23 0x77 -#define KS_GAMMAD24 0x78 -#define KS_GAMMAD25 0x79 -#define KS_GAMMAD26 0x7a -#define KS_GAMMAD27 0x7b -#define KS_GAMMAD28 0x7c -#define KS_GAMMAD29 0x7d -#define KS_GAMMAD30 0x7e -#define KS_GAMMAD31 0x7f - - -/**************************************************************************** -* mga_dev : represents one ks0127 chip. -****************************************************************************/ - -struct adjust { - int contrast; - int bright; - int hue; - int ugain; - int vgain; -}; - -struct ks0127 { - struct v4l2_subdev sd; - v4l2_std_id norm; - int ident; - u8 regs[256]; -}; - -static inline struct ks0127 *to_ks0127(struct v4l2_subdev *sd) -{ - return container_of(sd, struct ks0127, sd); -} - - -static int debug; /* insmod parameter */ - -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug output"); - -static u8 reg_defaults[64]; - -static void init_reg_defaults(void) -{ - static int initialized; - u8 *table = reg_defaults; - - if (initialized) - return; - initialized = 1; - - table[KS_CMDA] = 0x2c; /* VSE=0, CCIR 601, autodetect standard */ - table[KS_CMDB] = 0x12; /* VALIGN=0, AGC control and input */ - table[KS_CMDC] = 0x00; /* Test options */ - /* clock & input select, write 1 to PORTA */ - table[KS_CMDD] = 0x01; - table[KS_HAVB] = 0x00; /* HAV Start Control */ - table[KS_HAVE] = 0x00; /* HAV End Control */ - table[KS_HS1B] = 0x10; /* HS1 Start Control */ - table[KS_HS1E] = 0x00; /* HS1 End Control */ - table[KS_HS2B] = 0x00; /* HS2 Start Control */ - table[KS_HS2E] = 0x00; /* HS2 End Control */ - table[KS_AGC] = 0x53; /* Manual setting for AGC */ - table[KS_HXTRA] = 0x00; /* Extra Bits for HAV and HS1/2 */ - table[KS_CDEM] = 0x00; /* Chroma Demodulation Control */ - table[KS_PORTAB] = 0x0f; /* port B is input, port A output GPPORT */ - table[KS_LUMA] = 0x01; /* Luma control */ - table[KS_CON] = 0x00; /* Contrast Control */ - table[KS_BRT] = 0x00; /* Brightness Control */ - table[KS_CHROMA] = 0x2a; /* Chroma control A */ - table[KS_CHROMB] = 0x90; /* Chroma control B */ - table[KS_DEMOD] = 0x00; /* Chroma Demodulation Control & Status */ - table[KS_SAT] = 0x00; /* Color Saturation Control*/ - table[KS_HUE] = 0x00; /* Hue Control */ - table[KS_VERTIA] = 0x00; /* Vertical Processing Control A */ - /* Vertical Processing Control B, luma 1 line delayed */ - table[KS_VERTIB] = 0x12; - table[KS_VERTIC] = 0x0b; /* Vertical Processing Control C */ - table[KS_HSCLL] = 0x00; /* Horizontal Scaling Ratio Low */ - table[KS_HSCLH] = 0x00; /* Horizontal Scaling Ratio High */ - table[KS_VSCLL] = 0x00; /* Vertical Scaling Ratio Low */ - table[KS_VSCLH] = 0x00; /* Vertical Scaling Ratio High */ - /* 16 bit YCbCr 4:2:2 output; I can't make the bt866 like 8 bit /Sam */ - table[KS_OFMTA] = 0x30; - table[KS_OFMTB] = 0x00; /* Output Control B */ - /* VBI Decoder Control; 4bit fmt: avoid Y overflow */ - table[KS_VBICTL] = 0x5d; - table[KS_CCDAT2] = 0x00; /* Read Only register */ - table[KS_CCDAT1] = 0x00; /* Read Only register */ - table[KS_VBIL30] = 0xa8; /* VBI data decoding options */ - table[KS_VBIL74] = 0xaa; /* VBI data decoding options */ - table[KS_VBIL118] = 0x2a; /* VBI data decoding options */ - table[KS_VBIL1512] = 0x00; /* VBI data decoding options */ - table[KS_TTFRAM] = 0x00; /* Teletext frame alignment pattern */ - table[KS_TESTA] = 0x00; /* test register, shouldn't be written */ - table[KS_UVOFFH] = 0x00; /* UV Offset Adjustment High */ - table[KS_UVOFFL] = 0x00; /* UV Offset Adjustment Low */ - table[KS_UGAIN] = 0x00; /* U Component Gain Adjustment */ - table[KS_VGAIN] = 0x00; /* V Component Gain Adjustment */ - table[KS_VAVB] = 0x07; /* VAV Begin */ - table[KS_VAVE] = 0x00; /* VAV End */ - table[KS_CTRACK] = 0x00; /* Chroma Tracking Control */ - table[KS_POLCTL] = 0x41; /* Timing Signal Polarity Control */ - table[KS_REFCOD] = 0x80; /* Reference Code Insertion Control */ - table[KS_INVALY] = 0x10; /* Invalid Y Code */ - table[KS_INVALU] = 0x80; /* Invalid U Code */ - table[KS_INVALV] = 0x80; /* Invalid V Code */ - table[KS_UNUSEY] = 0x10; /* Unused Y Code */ - table[KS_UNUSEU] = 0x80; /* Unused U Code */ - table[KS_UNUSEV] = 0x80; /* Unused V Code */ - table[KS_USRSAV] = 0x00; /* reserved */ - table[KS_USREAV] = 0x00; /* reserved */ - table[KS_SHS1A] = 0x00; /* User Defined SHS1 A */ - /* User Defined SHS1 B, ALT656=1 on 0127B */ - table[KS_SHS1B] = 0x80; - table[KS_SHS1C] = 0x00; /* User Defined SHS1 C */ - table[KS_CMDE] = 0x00; /* Command Register E */ - table[KS_VSDEL] = 0x00; /* VS Delay Control */ - /* Command Register F, update -immediately- */ - /* (there might come no vsync)*/ - table[KS_CMDF] = 0x02; -} - - -/* We need to manually read because of a bug in the KS0127 chip. - * - * An explanation from kayork@mail.utexas.edu: - * - * During I2C reads, the KS0127 only samples for a stop condition - * during the place where the acknowledge bit should be. Any standard - * I2C implementation (correctly) throws in another clock transition - * at the 9th bit, and the KS0127 will not recognize the stop condition - * and will continue to clock out data. - * - * So we have to do the read ourself. Big deal. - * workaround in i2c-algo-bit - */ - - -static u8 ks0127_read(struct v4l2_subdev *sd, u8 reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - char val = 0; - struct i2c_msg msgs[] = { - { client->addr, 0, sizeof(reg), ® }, - { client->addr, I2C_M_RD | I2C_M_NO_RD_ACK, sizeof(val), &val } - }; - int ret; - - ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); - if (ret != ARRAY_SIZE(msgs)) - v4l2_dbg(1, debug, sd, "read error\n"); - - return val; -} - - -static void ks0127_write(struct v4l2_subdev *sd, u8 reg, u8 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ks0127 *ks = to_ks0127(sd); - char msg[] = { reg, val }; - - if (i2c_master_send(client, msg, sizeof(msg)) != sizeof(msg)) - v4l2_dbg(1, debug, sd, "write error\n"); - - ks->regs[reg] = val; -} - - -/* generic bit-twiddling */ -static void ks0127_and_or(struct v4l2_subdev *sd, u8 reg, u8 and_v, u8 or_v) -{ - struct ks0127 *ks = to_ks0127(sd); - - u8 val = ks->regs[reg]; - val = (val & and_v) | or_v; - ks0127_write(sd, reg, val); -} - - - -/**************************************************************************** -* ks0127 private api -****************************************************************************/ -static void ks0127_init(struct v4l2_subdev *sd) -{ - struct ks0127 *ks = to_ks0127(sd); - u8 *table = reg_defaults; - int i; - - ks->ident = V4L2_IDENT_KS0127; - - v4l2_dbg(1, debug, sd, "reset\n"); - msleep(1); - - /* initialize all registers to known values */ - /* (except STAT, 0x21, 0x22, TEST and 0x38,0x39) */ - - for (i = 1; i < 33; i++) - ks0127_write(sd, i, table[i]); - - for (i = 35; i < 40; i++) - ks0127_write(sd, i, table[i]); - - for (i = 41; i < 56; i++) - ks0127_write(sd, i, table[i]); - - for (i = 58; i < 64; i++) - ks0127_write(sd, i, table[i]); - - - if ((ks0127_read(sd, KS_STAT) & 0x80) == 0) { - ks->ident = V4L2_IDENT_KS0122S; - v4l2_dbg(1, debug, sd, "ks0122s found\n"); - return; - } - - switch (ks0127_read(sd, KS_CMDE) & 0x0f) { - case 0: - v4l2_dbg(1, debug, sd, "ks0127 found\n"); - break; - - case 9: - ks->ident = V4L2_IDENT_KS0127B; - v4l2_dbg(1, debug, sd, "ks0127B Revision A found\n"); - break; - - default: - v4l2_dbg(1, debug, sd, "unknown revision\n"); - break; - } -} - -static int ks0127_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct ks0127 *ks = to_ks0127(sd); - - switch (input) { - case KS_INPUT_COMPOSITE_1: - case KS_INPUT_COMPOSITE_2: - case KS_INPUT_COMPOSITE_3: - case KS_INPUT_COMPOSITE_4: - case KS_INPUT_COMPOSITE_5: - case KS_INPUT_COMPOSITE_6: - v4l2_dbg(1, debug, sd, - "s_routing %d: Composite\n", input); - /* autodetect 50/60 Hz */ - ks0127_and_or(sd, KS_CMDA, 0xfc, 0x00); - /* VSE=0 */ - ks0127_and_or(sd, KS_CMDA, ~0x40, 0x00); - /* set input line */ - ks0127_and_or(sd, KS_CMDB, 0xb0, input); - /* non-freerunning mode */ - ks0127_and_or(sd, KS_CMDC, 0x70, 0x0a); - /* analog input */ - ks0127_and_or(sd, KS_CMDD, 0x03, 0x00); - /* enable chroma demodulation */ - ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x00); - /* chroma trap, HYBWR=1 */ - ks0127_and_or(sd, KS_LUMA, 0x00, - (reg_defaults[KS_LUMA])|0x0c); - /* scaler fullbw, luma comb off */ - ks0127_and_or(sd, KS_VERTIA, 0x08, 0x81); - /* manual chroma comb .25 .5 .25 */ - ks0127_and_or(sd, KS_VERTIC, 0x0f, 0x90); - - /* chroma path delay */ - ks0127_and_or(sd, KS_CHROMB, 0x0f, 0x90); - - ks0127_write(sd, KS_UGAIN, reg_defaults[KS_UGAIN]); - ks0127_write(sd, KS_VGAIN, reg_defaults[KS_VGAIN]); - ks0127_write(sd, KS_UVOFFH, reg_defaults[KS_UVOFFH]); - ks0127_write(sd, KS_UVOFFL, reg_defaults[KS_UVOFFL]); - break; - - case KS_INPUT_SVIDEO_1: - case KS_INPUT_SVIDEO_2: - case KS_INPUT_SVIDEO_3: - v4l2_dbg(1, debug, sd, - "s_routing %d: S-Video\n", input); - /* autodetect 50/60 Hz */ - ks0127_and_or(sd, KS_CMDA, 0xfc, 0x00); - /* VSE=0 */ - ks0127_and_or(sd, KS_CMDA, ~0x40, 0x00); - /* set input line */ - ks0127_and_or(sd, KS_CMDB, 0xb0, input); - /* non-freerunning mode */ - ks0127_and_or(sd, KS_CMDC, 0x70, 0x0a); - /* analog input */ - ks0127_and_or(sd, KS_CMDD, 0x03, 0x00); - /* enable chroma demodulation */ - ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x00); - ks0127_and_or(sd, KS_LUMA, 0x00, - reg_defaults[KS_LUMA]); - /* disable luma comb */ - ks0127_and_or(sd, KS_VERTIA, 0x08, - (reg_defaults[KS_VERTIA]&0xf0)|0x01); - ks0127_and_or(sd, KS_VERTIC, 0x0f, - reg_defaults[KS_VERTIC]&0xf0); - - ks0127_and_or(sd, KS_CHROMB, 0x0f, - reg_defaults[KS_CHROMB]&0xf0); - - ks0127_write(sd, KS_UGAIN, reg_defaults[KS_UGAIN]); - ks0127_write(sd, KS_VGAIN, reg_defaults[KS_VGAIN]); - ks0127_write(sd, KS_UVOFFH, reg_defaults[KS_UVOFFH]); - ks0127_write(sd, KS_UVOFFL, reg_defaults[KS_UVOFFL]); - break; - - case KS_INPUT_YUV656: - v4l2_dbg(1, debug, sd, "s_routing 15: YUV656\n"); - if (ks->norm & V4L2_STD_525_60) - /* force 60 Hz */ - ks0127_and_or(sd, KS_CMDA, 0xfc, 0x03); - else - /* force 50 Hz */ - ks0127_and_or(sd, KS_CMDA, 0xfc, 0x02); - - ks0127_and_or(sd, KS_CMDA, 0xff, 0x40); /* VSE=1 */ - /* set input line and VALIGN */ - ks0127_and_or(sd, KS_CMDB, 0xb0, (input | 0x40)); - /* freerunning mode, */ - /* TSTGEN = 1 TSTGFR=11 TSTGPH=0 TSTGPK=0 VMEM=1*/ - ks0127_and_or(sd, KS_CMDC, 0x70, 0x87); - /* digital input, SYNDIR = 0 INPSL=01 CLKDIR=0 EAV=0 */ - ks0127_and_or(sd, KS_CMDD, 0x03, 0x08); - /* disable chroma demodulation */ - ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x30); - /* HYPK =01 CTRAP = 0 HYBWR=0 PED=1 RGBH=1 UNIT=1 */ - ks0127_and_or(sd, KS_LUMA, 0x00, 0x71); - ks0127_and_or(sd, KS_VERTIC, 0x0f, - reg_defaults[KS_VERTIC]&0xf0); - - /* scaler fullbw, luma comb off */ - ks0127_and_or(sd, KS_VERTIA, 0x08, 0x81); - - ks0127_and_or(sd, KS_CHROMB, 0x0f, - reg_defaults[KS_CHROMB]&0xf0); - - ks0127_and_or(sd, KS_CON, 0x00, 0x00); - ks0127_and_or(sd, KS_BRT, 0x00, 32); /* spec: 34 */ - /* spec: 229 (e5) */ - ks0127_and_or(sd, KS_SAT, 0x00, 0xe8); - ks0127_and_or(sd, KS_HUE, 0x00, 0); - - ks0127_and_or(sd, KS_UGAIN, 0x00, 238); - ks0127_and_or(sd, KS_VGAIN, 0x00, 0x00); - - /*UOFF:0x30, VOFF:0x30, TSTCGN=1 */ - ks0127_and_or(sd, KS_UVOFFH, 0x00, 0x4f); - ks0127_and_or(sd, KS_UVOFFL, 0x00, 0x00); - break; - - default: - v4l2_dbg(1, debug, sd, - "s_routing: Unknown input %d\n", input); - break; - } - - /* hack: CDMLPF sometimes spontaneously switches on; */ - /* force back off */ - ks0127_write(sd, KS_DEMOD, reg_defaults[KS_DEMOD]); - return 0; -} - -static int ks0127_s_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct ks0127 *ks = to_ks0127(sd); - - /* Set to automatic SECAM/Fsc mode */ - ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x00); - - ks->norm = std; - if (std & V4L2_STD_NTSC) { - v4l2_dbg(1, debug, sd, - "s_std: NTSC_M\n"); - ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x20); - } else if (std & V4L2_STD_PAL_N) { - v4l2_dbg(1, debug, sd, - "s_std: NTSC_N (fixme)\n"); - ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x40); - } else if (std & V4L2_STD_PAL) { - v4l2_dbg(1, debug, sd, - "s_std: PAL_N\n"); - ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x20); - } else if (std & V4L2_STD_PAL_M) { - v4l2_dbg(1, debug, sd, - "s_std: PAL_M (fixme)\n"); - ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x40); - } else if (std & V4L2_STD_SECAM) { - v4l2_dbg(1, debug, sd, - "s_std: SECAM\n"); - - /* set to secam autodetection */ - ks0127_and_or(sd, KS_CHROMA, 0xdf, 0x20); - ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x00); - schedule_timeout_interruptible(HZ/10+1); - - /* did it autodetect? */ - if (!(ks0127_read(sd, KS_DEMOD) & 0x40)) - /* force to secam mode */ - ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x0f); - } else { - v4l2_dbg(1, debug, sd, "s_std: Unknown norm %llx\n", - (unsigned long long)std); - } - return 0; -} - -static int ks0127_s_stream(struct v4l2_subdev *sd, int enable) -{ - v4l2_dbg(1, debug, sd, "s_stream(%d)\n", enable); - if (enable) { - /* All output pins on */ - ks0127_and_or(sd, KS_OFMTA, 0xcf, 0x30); - /* Obey the OEN pin */ - ks0127_and_or(sd, KS_CDEM, 0x7f, 0x00); - } else { - /* Video output pins off */ - ks0127_and_or(sd, KS_OFMTA, 0xcf, 0x00); - /* Ignore the OEN pin */ - ks0127_and_or(sd, KS_CDEM, 0x7f, 0x80); - } - return 0; -} - -static int ks0127_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd) -{ - int stat = V4L2_IN_ST_NO_SIGNAL; - u8 status; - v4l2_std_id std = V4L2_STD_ALL; - - status = ks0127_read(sd, KS_STAT); - if (!(status & 0x20)) /* NOVID not set */ - stat = 0; - if (!(status & 0x01)) /* CLOCK set */ - stat |= V4L2_IN_ST_NO_COLOR; - if ((status & 0x08)) /* PALDET set */ - std = V4L2_STD_PAL; - else - std = V4L2_STD_NTSC; - if (pstd) - *pstd = std; - if (pstatus) - *pstatus = stat; - return 0; -} - -static int ks0127_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) -{ - v4l2_dbg(1, debug, sd, "querystd\n"); - return ks0127_status(sd, NULL, std); -} - -static int ks0127_g_input_status(struct v4l2_subdev *sd, u32 *status) -{ - v4l2_dbg(1, debug, sd, "g_input_status\n"); - return ks0127_status(sd, status, NULL); -} - -static int ks0127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ks0127 *ks = to_ks0127(sd); - - return v4l2_chip_ident_i2c_client(client, chip, ks->ident, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops ks0127_core_ops = { - .g_chip_ident = ks0127_g_chip_ident, - .s_std = ks0127_s_std, -}; - -static const struct v4l2_subdev_video_ops ks0127_video_ops = { - .s_routing = ks0127_s_routing, - .s_stream = ks0127_s_stream, - .querystd = ks0127_querystd, - .g_input_status = ks0127_g_input_status, -}; - -static const struct v4l2_subdev_ops ks0127_ops = { - .core = &ks0127_core_ops, - .video = &ks0127_video_ops, -}; - -/* ----------------------------------------------------------------------- */ - - -static int ks0127_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ - struct ks0127 *ks; - struct v4l2_subdev *sd; - - v4l_info(client, "%s chip found @ 0x%x (%s)\n", - client->addr == (I2C_KS0127_ADDON >> 1) ? "addon" : "on-board", - client->addr << 1, client->adapter->name); - - ks = kzalloc(sizeof(*ks), GFP_KERNEL); - if (ks == NULL) - return -ENOMEM; - sd = &ks->sd; - v4l2_i2c_subdev_init(sd, client, &ks0127_ops); - - /* power up */ - init_reg_defaults(); - ks0127_write(sd, KS_CMDA, 0x2c); - mdelay(10); - - /* reset the device */ - ks0127_init(sd); - return 0; -} - -static int ks0127_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - ks0127_write(sd, KS_OFMTA, 0x20); /* tristate */ - ks0127_write(sd, KS_CMDA, 0x2c | 0x80); /* power down */ - kfree(to_ks0127(sd)); - return 0; -} - -static const struct i2c_device_id ks0127_id[] = { - { "ks0127", 0 }, - { "ks0127b", 0 }, - { "ks0122s", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ks0127_id); - -static struct i2c_driver ks0127_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "ks0127", - }, - .probe = ks0127_probe, - .remove = ks0127_remove, - .id_table = ks0127_id, -}; - -module_i2c_driver(ks0127_driver); diff --git a/drivers/media/video/ks0127.h b/drivers/media/video/ks0127.h deleted file mode 100644 index cb8abd5403b..00000000000 --- a/drivers/media/video/ks0127.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Video Capture Driver ( Video for Linux 1/2 ) - * for the Matrox Marvel G200,G400 and Rainbow Runner-G series - * - * This module is an interface to the KS0127 video decoder chip. - * - * Copyright (C) 1999 Ryan Drake - * - * 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. - */ - -#ifndef KS0127_H -#define KS0127_H - -/* input channels */ -#define KS_INPUT_COMPOSITE_1 0 -#define KS_INPUT_COMPOSITE_2 1 -#define KS_INPUT_COMPOSITE_3 2 -#define KS_INPUT_COMPOSITE_4 4 -#define KS_INPUT_COMPOSITE_5 5 -#define KS_INPUT_COMPOSITE_6 6 - -#define KS_INPUT_SVIDEO_1 8 -#define KS_INPUT_SVIDEO_2 9 -#define KS_INPUT_SVIDEO_3 10 - -#define KS_INPUT_YUV656 15 -#define KS_INPUT_COUNT 10 - -/* output channels */ -#define KS_OUTPUT_YUV656E 0 -#define KS_OUTPUT_EXV 1 - -/* video standards */ -#define KS_STD_NTSC_N 112 /* 50 Hz NTSC */ -#define KS_STD_PAL_M 113 /* 60 Hz PAL */ - -#endif /* KS0127_H */ - diff --git a/drivers/media/video/m52790.c b/drivers/media/video/m52790.c deleted file mode 100644 index 0991576f4c8..00000000000 --- a/drivers/media/video/m52790.c +++ /dev/null @@ -1,216 +0,0 @@ -/* - * m52790 i2c ivtv driver. - * Copyright (C) 2007 Hans Verkuil - * - * A/V source switching Mitsubishi M52790SP/FP - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("i2c device driver for m52790 A/V switch"); -MODULE_AUTHOR("Hans Verkuil"); -MODULE_LICENSE("GPL"); - - -struct m52790_state { - struct v4l2_subdev sd; - u16 input; - u16 output; -}; - -static inline struct m52790_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct m52790_state, sd); -} - -/* ----------------------------------------------------------------------- */ - -static int m52790_write(struct v4l2_subdev *sd) -{ - struct m52790_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - u8 sw1 = (state->input | state->output) & 0xff; - u8 sw2 = (state->input | state->output) >> 8; - - return i2c_smbus_write_byte_data(client, sw1, sw2); -} - -/* Note: audio and video are linked and cannot be switched separately. - So audio and video routing commands are identical for this chip. - In theory the video amplifier and audio modes could be handled - separately for the output, but that seems to be overkill right now. - The same holds for implementing an audio mute control, this is now - part of the audio output routing. The normal case is that another - chip takes care of the actual muting so making it part of the - output routing seems to be the right thing to do for now. */ -static int m52790_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct m52790_state *state = to_state(sd); - - state->input = input; - state->output = output; - m52790_write(sd); - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int m52790_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct m52790_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (reg->reg != 0) - return -EINVAL; - reg->size = 1; - reg->val = state->input | state->output; - return 0; -} - -static int m52790_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct m52790_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (reg->reg != 0) - return -EINVAL; - state->input = reg->val & 0x0303; - state->output = reg->val & ~0x0303; - m52790_write(sd); - return 0; -} -#endif - -static int m52790_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_M52790, 0); -} - -static int m52790_log_status(struct v4l2_subdev *sd) -{ - struct m52790_state *state = to_state(sd); - - v4l2_info(sd, "Switch 1: %02x\n", - (state->input | state->output) & 0xff); - v4l2_info(sd, "Switch 2: %02x\n", - (state->input | state->output) >> 8); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops m52790_core_ops = { - .log_status = m52790_log_status, - .g_chip_ident = m52790_g_chip_ident, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = m52790_g_register, - .s_register = m52790_s_register, -#endif -}; - -static const struct v4l2_subdev_audio_ops m52790_audio_ops = { - .s_routing = m52790_s_routing, -}; - -static const struct v4l2_subdev_video_ops m52790_video_ops = { - .s_routing = m52790_s_routing, -}; - -static const struct v4l2_subdev_ops m52790_ops = { - .core = &m52790_core_ops, - .audio = &m52790_audio_ops, - .video = &m52790_video_ops, -}; - -/* ----------------------------------------------------------------------- */ - -/* i2c implementation */ - -static int m52790_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct m52790_state *state; - struct v4l2_subdev *sd; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - state = kzalloc(sizeof(struct m52790_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &m52790_ops); - state->input = M52790_IN_TUNER; - state->output = M52790_OUT_STEREO; - m52790_write(sd); - return 0; -} - -static int m52790_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 m52790_id[] = { - { "m52790", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, m52790_id); - -static struct i2c_driver m52790_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "m52790", - }, - .probe = m52790_probe, - .remove = m52790_remove, - .id_table = m52790_id, -}; - -module_i2c_driver(m52790_driver); diff --git a/drivers/media/video/m5mols/Kconfig b/drivers/media/video/m5mols/Kconfig deleted file mode 100644 index dc8c2505907..00000000000 --- a/drivers/media/video/m5mols/Kconfig +++ /dev/null @@ -1,6 +0,0 @@ -config VIDEO_M5MOLS - tristate "Fujitsu M-5MOLS 8MP sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT - ---help--- - This driver supports Fujitsu M-5MOLS camera sensor with ISP diff --git a/drivers/media/video/m5mols/Makefile b/drivers/media/video/m5mols/Makefile deleted file mode 100644 index 0a44e028edc..00000000000 --- a/drivers/media/video/m5mols/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -m5mols-objs := m5mols_core.o m5mols_controls.o m5mols_capture.o - -obj-$(CONFIG_VIDEO_M5MOLS) += m5mols.o diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h deleted file mode 100644 index bb589917b65..00000000000 --- a/drivers/media/video/m5mols/m5mols.h +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Header for M-5MOLS 8M Pixel camera sensor with ISP - * - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * Author: HeungJun Kim - * - * Copyright (C) 2009 Samsung Electronics Co., Ltd. - * Author: Dongsoo Nathaniel Kim - * - * 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. - */ - -#ifndef M5MOLS_H -#define M5MOLS_H - -#include -#include "m5mols_reg.h" - -extern int m5mols_debug; - -enum m5mols_restype { - M5MOLS_RESTYPE_MONITOR, - M5MOLS_RESTYPE_CAPTURE, - M5MOLS_RESTYPE_MAX, -}; - -/** - * struct m5mols_resolution - structure for the resolution - * @type: resolution type according to the pixel code - * @width: width of the resolution - * @height: height of the resolution - * @reg: resolution preset register value - */ -struct m5mols_resolution { - u8 reg; - enum m5mols_restype type; - u16 width; - u16 height; -}; - -/** - * struct m5mols_exif - structure for the EXIF information of M-5MOLS - * @exposure_time: exposure time register value - * @shutter_speed: speed of the shutter register value - * @aperture: aperture register value - * @exposure_bias: it calls also EV bias - * @iso_speed: ISO register value - * @flash: status register value of the flash - * @sdr: status register value of the Subject Distance Range - * @qval: not written exact meaning in document - */ -struct m5mols_exif { - u32 exposure_time; - u32 shutter_speed; - u32 aperture; - u32 brightness; - u32 exposure_bias; - u16 iso_speed; - u16 flash; - u16 sdr; - u16 qval; -}; - -/** - * struct m5mols_capture - Structure for the capture capability - * @exif: EXIF information - * @main: size in bytes of the main image - * @thumb: size in bytes of the thumb image, if it was accompanied - * @total: total size in bytes of the produced image - */ -struct m5mols_capture { - struct m5mols_exif exif; - u32 main; - u32 thumb; - u32 total; -}; - -/** - * struct m5mols_scenemode - structure for the scenemode capability - * @metering: metering light register value - * @ev_bias: EV bias register value - * @wb_mode: mode which means the WhiteBalance is Auto or Manual - * @wb_preset: whitebalance preset register value in the Manual mode - * @chroma_en: register value whether the Chroma capability is enabled or not - * @chroma_lvl: chroma's level register value - * @edge_en: register value Whether the Edge capability is enabled or not - * @edge_lvl: edge's level register value - * @af_range: Auto Focus's range - * @fd_mode: Face Detection mode - * @mcc: Multi-axis Color Conversion which means emotion color - * @light: status of the Light - * @flash: status of the Flash - * @tone: Tone color which means Contrast - * @iso: ISO register value - * @capt_mode: Mode of the Image Stabilization while the camera capturing - * @wdr: Wide Dynamic Range register value - * - * The each value according to each scenemode is recommended in the documents. - */ -struct m5mols_scenemode { - u8 metering; - u8 ev_bias; - u8 wb_mode; - u8 wb_preset; - u8 chroma_en; - u8 chroma_lvl; - u8 edge_en; - u8 edge_lvl; - u8 af_range; - u8 fd_mode; - u8 mcc; - u8 light; - u8 flash; - u8 tone; - u8 iso; - u8 capt_mode; - u8 wdr; -}; - -/** - * struct m5mols_version - firmware version information - * @customer: customer information - * @project: version of project information according to customer - * @fw: firmware revision - * @hw: hardware revision - * @param: version of the parameter - * @awb: Auto WhiteBalance algorithm version - * @str: information about manufacturer and packaging vendor - * @af: Auto Focus version - * - * The register offset starts the customer version at 0x0, and it ends - * the awb version at 0x09. The customer, project information occupies 1 bytes - * each. And also the fw, hw, param, awb each requires 2 bytes. The str is - * unique string associated with firmware's version. It includes information - * about manufacturer and the vendor of the sensor's packaging. The least - * significant 2 bytes of the string indicate packaging manufacturer. - */ -#define VERSION_STRING_SIZE 22 -struct m5mols_version { - u8 customer; - u8 project; - u16 fw; - u16 hw; - u16 param; - u16 awb; - u8 str[VERSION_STRING_SIZE]; - u8 af; -}; - -/** - * struct m5mols_info - M-5MOLS driver data structure - * @pdata: platform data - * @sd: v4l-subdev instance - * @pad: media pad - * @ffmt: current fmt according to resolution type - * @res_type: current resolution type - * @irq_waitq: waitqueue for the capture - * @irq_done: set to 1 in the interrupt handler - * @handle: control handler - * @auto_exposure: auto/manual exposure control - * @exposure_bias: exposure compensation control - * @exposure: manual exposure control - * @metering: exposure metering control - * @auto_iso: auto/manual ISO sensitivity control - * @iso: manual ISO sensitivity control - * @auto_wb: auto white balance control - * @lock_3a: 3A lock control - * @colorfx: color effect control - * @saturation: saturation control - * @zoom: zoom control - * @wdr: wide dynamic range control - * @stabilization: image stabilization control - * @jpeg_quality: JPEG compression quality control - * @ver: information of the version - * @cap: the capture mode attributes - * @isp_ready: 1 when the ISP controller has completed booting - * @power: current sensor's power status - * @ctrl_sync: 1 when the control handler state is restored in H/W - * @resolution: register value for current resolution - * @mode: register value for current operation mode - * @set_power: optional power callback to the board code - */ -struct m5mols_info { - const struct m5mols_platform_data *pdata; - struct v4l2_subdev sd; - struct media_pad pad; - struct v4l2_mbus_framefmt ffmt[M5MOLS_RESTYPE_MAX]; - int res_type; - - wait_queue_head_t irq_waitq; - atomic_t irq_done; - - struct v4l2_ctrl_handler handle; - struct { - /* exposure/exposure bias/auto exposure cluster */ - struct v4l2_ctrl *auto_exposure; - struct v4l2_ctrl *exposure_bias; - struct v4l2_ctrl *exposure; - struct v4l2_ctrl *metering; - }; - struct { - /* iso/auto iso cluster */ - struct v4l2_ctrl *auto_iso; - struct v4l2_ctrl *iso; - }; - struct v4l2_ctrl *auto_wb; - - struct v4l2_ctrl *lock_3a; - struct v4l2_ctrl *colorfx; - struct v4l2_ctrl *saturation; - struct v4l2_ctrl *zoom; - struct v4l2_ctrl *wdr; - struct v4l2_ctrl *stabilization; - struct v4l2_ctrl *jpeg_quality; - - struct m5mols_version ver; - struct m5mols_capture cap; - - unsigned int isp_ready:1; - unsigned int power:1; - unsigned int ctrl_sync:1; - - u8 resolution; - u8 mode; - - int (*set_power)(struct device *dev, int on); -}; - -#define is_available_af(__info) (__info->ver.af) -#define is_code(__code, __type) (__code == m5mols_default_ffmt[__type].code) -#define is_manufacturer(__info, __manufacturer) \ - (__info->ver.str[0] == __manufacturer[0] && \ - __info->ver.str[1] == __manufacturer[1]) -/* - * I2C operation of the M-5MOLS - * - * The I2C read operation of the M-5MOLS requires 2 messages. The first - * message sends the information about the command, command category, and total - * message size. The second message is used to retrieve the data specifed in - * the first message - * - * 1st message 2nd message - * +-------+---+----------+-----+-------+ +------+------+------+------+ - * | size1 | R | category | cmd | size2 | | d[0] | d[1] | d[2] | d[3] | - * +-------+---+----------+-----+-------+ +------+------+------+------+ - * - size1: message data size(5 in this case) - * - size2: desired buffer size of the 2nd message - * - d[0..3]: according to size2 - * - * The I2C write operation needs just one message. The message includes - * category, command, total size, and desired data. - * - * 1st message - * +-------+---+----------+-----+------+------+------+------+ - * | size1 | W | category | cmd | d[0] | d[1] | d[2] | d[3] | - * +-------+---+----------+-----+------+------+------+------+ - * - d[0..3]: according to size1 - */ -int m5mols_read_u8(struct v4l2_subdev *sd, u32 reg_comb, u8 *val); -int m5mols_read_u16(struct v4l2_subdev *sd, u32 reg_comb, u16 *val); -int m5mols_read_u32(struct v4l2_subdev *sd, u32 reg_comb, u32 *val); -int m5mols_write(struct v4l2_subdev *sd, u32 reg_comb, u32 val); - -int m5mols_busy_wait(struct v4l2_subdev *sd, u32 reg, u32 value, u32 mask, - int timeout); - -/* Mask value for busy waiting until M-5MOLS I2C interface is initialized */ -#define M5MOLS_I2C_RDY_WAIT_FL (1 << 16) -/* ISP state transition timeout, in ms */ -#define M5MOLS_MODE_CHANGE_TIMEOUT 200 -#define M5MOLS_BUSY_WAIT_DEF_TIMEOUT 250 - -/* - * Mode operation of the M-5MOLS - * - * Changing the mode of the M-5MOLS is needed right executing order. - * There are three modes(PARAMETER, MONITOR, CAPTURE) which can be changed - * by user. There are various categories associated with each mode. - * - * +============================================================+ - * | mode | category | - * +============================================================+ - * | FLASH | FLASH(only after Stand-by or Power-on) | - * | SYSTEM | SYSTEM(only after sensor arm-booting) | - * | PARAMETER | PARAMETER | - * | MONITOR | MONITOR(preview), Auto Focus, Face Detection | - * | CAPTURE | Single CAPTURE, Preview(recording) | - * +============================================================+ - * - * The available executing order between each modes are as follows: - * PARAMETER <---> MONITOR <---> CAPTURE - */ -int m5mols_set_mode(struct m5mols_info *info, u8 mode); - -int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg); -int m5mols_wait_interrupt(struct v4l2_subdev *sd, u8 condition, u32 timeout); -int m5mols_restore_controls(struct m5mols_info *info); -int m5mols_start_capture(struct m5mols_info *info); -int m5mols_do_scenemode(struct m5mols_info *info, u8 mode); -int m5mols_lock_3a(struct m5mols_info *info, bool lock); -int m5mols_set_ctrl(struct v4l2_ctrl *ctrl); -int m5mols_init_controls(struct v4l2_subdev *sd); - -/* The firmware function */ -int m5mols_update_fw(struct v4l2_subdev *sd, - int (*set_power)(struct m5mols_info *, bool)); - -static inline struct m5mols_info *to_m5mols(struct v4l2_subdev *subdev) -{ - return container_of(subdev, struct m5mols_info, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - struct m5mols_info *info = container_of(ctrl->handler, - struct m5mols_info, handle); - return &info->sd; -} - -static inline void m5mols_set_ctrl_mode(struct v4l2_ctrl *ctrl, - unsigned int mode) -{ - ctrl->priv = (void *)mode; -} - -static inline unsigned int m5mols_get_ctrl_mode(struct v4l2_ctrl *ctrl) -{ - return (unsigned int)ctrl->priv; -} - -#endif /* M5MOLS_H */ diff --git a/drivers/media/video/m5mols/m5mols_capture.c b/drivers/media/video/m5mols/m5mols_capture.c deleted file mode 100644 index cb243bd278c..00000000000 --- a/drivers/media/video/m5mols/m5mols_capture.c +++ /dev/null @@ -1,155 +0,0 @@ - -/* - * The Capture code for Fujitsu M-5MOLS ISP - * - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * Author: HeungJun Kim - * - * Copyright (C) 2009 Samsung Electronics Co., Ltd. - * Author: Dongsoo Nathaniel Kim - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "m5mols.h" -#include "m5mols_reg.h" - -/** - * m5mols_read_rational - I2C read of a rational number - * - * Read numerator and denominator from registers @addr_num and @addr_den - * respectively and return the division result in @val. - */ -static int m5mols_read_rational(struct v4l2_subdev *sd, u32 addr_num, - u32 addr_den, u32 *val) -{ - u32 num, den; - - int ret = m5mols_read_u32(sd, addr_num, &num); - if (!ret) - ret = m5mols_read_u32(sd, addr_den, &den); - if (ret) - return ret; - *val = den == 0 ? 0 : num / den; - return ret; -} - -/** - * m5mols_capture_info - Gather captured image information - * - * For now it gathers only EXIF information and file size. - */ -static int m5mols_capture_info(struct m5mols_info *info) -{ - struct m5mols_exif *exif = &info->cap.exif; - struct v4l2_subdev *sd = &info->sd; - int ret; - - ret = m5mols_read_rational(sd, EXIF_INFO_EXPTIME_NU, - EXIF_INFO_EXPTIME_DE, &exif->exposure_time); - if (ret) - return ret; - ret = m5mols_read_rational(sd, EXIF_INFO_TV_NU, EXIF_INFO_TV_DE, - &exif->shutter_speed); - if (ret) - return ret; - ret = m5mols_read_rational(sd, EXIF_INFO_AV_NU, EXIF_INFO_AV_DE, - &exif->aperture); - if (ret) - return ret; - ret = m5mols_read_rational(sd, EXIF_INFO_BV_NU, EXIF_INFO_BV_DE, - &exif->brightness); - if (ret) - return ret; - ret = m5mols_read_rational(sd, EXIF_INFO_EBV_NU, EXIF_INFO_EBV_DE, - &exif->exposure_bias); - if (ret) - return ret; - - ret = m5mols_read_u16(sd, EXIF_INFO_ISO, &exif->iso_speed); - if (!ret) - ret = m5mols_read_u16(sd, EXIF_INFO_FLASH, &exif->flash); - if (!ret) - ret = m5mols_read_u16(sd, EXIF_INFO_SDR, &exif->sdr); - if (!ret) - ret = m5mols_read_u16(sd, EXIF_INFO_QVAL, &exif->qval); - if (ret) - return ret; - - if (!ret) - ret = m5mols_read_u32(sd, CAPC_IMAGE_SIZE, &info->cap.main); - if (!ret) - ret = m5mols_read_u32(sd, CAPC_THUMB_SIZE, &info->cap.thumb); - if (!ret) - info->cap.total = info->cap.main + info->cap.thumb; - - return ret; -} - -int m5mols_start_capture(struct m5mols_info *info) -{ - struct v4l2_subdev *sd = &info->sd; - int ret; - - /* - * Synchronize the controls, set the capture frame resolution and color - * format. The frame capture is initiated during switching from Monitor - * to Capture mode. - */ - ret = m5mols_set_mode(info, REG_MONITOR); - if (!ret) - ret = m5mols_restore_controls(info); - if (!ret) - ret = m5mols_write(sd, CAPP_YUVOUT_MAIN, REG_JPEG); - if (!ret) - ret = m5mols_write(sd, CAPP_MAIN_IMAGE_SIZE, info->resolution); - if (!ret) - ret = m5mols_set_mode(info, REG_CAPTURE); - if (!ret) - /* Wait until a frame is captured to ISP internal memory */ - ret = m5mols_wait_interrupt(sd, REG_INT_CAPTURE, 2000); - if (ret) - return ret; - - /* - * Initiate the captured data transfer to a MIPI-CSI receiver. - */ - ret = m5mols_write(sd, CAPC_SEL_FRAME, 1); - if (!ret) - ret = m5mols_write(sd, CAPC_START, REG_CAP_START_MAIN); - if (!ret) { - bool captured = false; - unsigned int size; - - /* Wait for the capture completion interrupt */ - ret = m5mols_wait_interrupt(sd, REG_INT_CAPTURE, 2000); - if (!ret) { - captured = true; - ret = m5mols_capture_info(info); - } - size = captured ? info->cap.main : 0; - v4l2_dbg(1, m5mols_debug, sd, "%s: size: %d, thumb.: %d B\n", - __func__, size, info->cap.thumb); - - v4l2_subdev_notify(sd, S5P_FIMC_TX_END_NOTIFY, &size); - } - - return ret; -} diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c deleted file mode 100644 index fdbc205a296..00000000000 --- a/drivers/media/video/m5mols/m5mols_controls.c +++ /dev/null @@ -1,628 +0,0 @@ -/* - * Controls for M-5MOLS 8M Pixel camera sensor with ISP - * - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * Author: HeungJun Kim - * - * Copyright (C) 2009 Samsung Electronics Co., Ltd. - * Author: Dongsoo Nathaniel Kim - * - * 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. - */ - -#include -#include -#include -#include - -#include "m5mols.h" -#include "m5mols_reg.h" - -static struct m5mols_scenemode m5mols_default_scenemode[] = { - [REG_SCENE_NORMAL] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 3, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_NORMAL, REG_LIGHT_OFF, REG_FLASH_OFF, - 5, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_PORTRAIT] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 3, REG_EDGE_ON, 4, - REG_AF_NORMAL, BIT_FD_EN | BIT_FD_DRAW_FACE_FRAME, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_LANDSCAPE] = { - REG_AE_ALL, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 4, REG_EDGE_ON, 6, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_SPORTS] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 3, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_PARTY_INDOOR] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 4, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_200, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_BEACH_SNOW] = { - REG_AE_CENTER, REG_AE_INDEX_10_POS, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 4, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_SUNSET] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET, - REG_AWB_DAYLIGHT, - REG_CHROMA_ON, 3, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_DAWN_DUSK] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET, - REG_AWB_FLUORESCENT_1, - REG_CHROMA_ON, 3, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_FALL] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 5, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_NIGHT] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 3, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_AGAINST_LIGHT] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 3, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_FIRE] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 3, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_TEXT] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 3, REG_EDGE_ON, 7, - REG_AF_MACRO, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_ANTI_SHAKE, REG_WDR_ON, - }, - [REG_SCENE_CANDLE] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 3, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, -}; - -/** - * m5mols_do_scenemode() - Change current scenemode - * @mode: Desired mode of the scenemode - * - * WARNING: The execution order is important. Do not change the order. - */ -int m5mols_do_scenemode(struct m5mols_info *info, u8 mode) -{ - struct v4l2_subdev *sd = &info->sd; - struct m5mols_scenemode scenemode = m5mols_default_scenemode[mode]; - int ret; - - if (mode > REG_SCENE_CANDLE) - return -EINVAL; - - ret = v4l2_ctrl_s_ctrl(info->lock_3a, 0); - if (!ret) - ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, mode); - if (!ret) - ret = m5mols_write(sd, AE_EV_PRESET_CAPTURE, mode); - if (!ret) - ret = m5mols_write(sd, AE_MODE, scenemode.metering); - if (!ret) - ret = m5mols_write(sd, AE_INDEX, scenemode.ev_bias); - if (!ret) - ret = m5mols_write(sd, AWB_MODE, scenemode.wb_mode); - if (!ret) - ret = m5mols_write(sd, AWB_MANUAL, scenemode.wb_preset); - if (!ret) - ret = m5mols_write(sd, MON_CHROMA_EN, scenemode.chroma_en); - if (!ret) - ret = m5mols_write(sd, MON_CHROMA_LVL, scenemode.chroma_lvl); - if (!ret) - ret = m5mols_write(sd, MON_EDGE_EN, scenemode.edge_en); - if (!ret) - ret = m5mols_write(sd, MON_EDGE_LVL, scenemode.edge_lvl); - if (!ret && is_available_af(info)) - ret = m5mols_write(sd, AF_MODE, scenemode.af_range); - if (!ret && is_available_af(info)) - ret = m5mols_write(sd, FD_CTL, scenemode.fd_mode); - if (!ret) - ret = m5mols_write(sd, MON_TONE_CTL, scenemode.tone); - if (!ret) - ret = m5mols_write(sd, AE_ISO, scenemode.iso); - if (!ret) - ret = m5mols_set_mode(info, REG_CAPTURE); - if (!ret) - ret = m5mols_write(sd, CAPP_WDR_EN, scenemode.wdr); - if (!ret) - ret = m5mols_write(sd, CAPP_MCC_MODE, scenemode.mcc); - if (!ret) - ret = m5mols_write(sd, CAPP_LIGHT_CTRL, scenemode.light); - if (!ret) - ret = m5mols_write(sd, CAPP_FLASH_CTRL, scenemode.flash); - if (!ret) - ret = m5mols_write(sd, CAPC_MODE, scenemode.capt_mode); - if (!ret) - ret = m5mols_set_mode(info, REG_MONITOR); - - return ret; -} - -static int m5mols_3a_lock(struct m5mols_info *info, struct v4l2_ctrl *ctrl) -{ - bool af_lock = ctrl->val & V4L2_LOCK_FOCUS; - int ret = 0; - - if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_EXPOSURE) { - bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE; - - ret = m5mols_write(&info->sd, AE_LOCK, ae_lock ? - REG_AE_LOCK : REG_AE_UNLOCK); - if (ret) - return ret; - } - - if (((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_WHITE_BALANCE) - && info->auto_wb->val) { - bool awb_lock = ctrl->val & V4L2_LOCK_WHITE_BALANCE; - - ret = m5mols_write(&info->sd, AWB_LOCK, awb_lock ? - REG_AWB_LOCK : REG_AWB_UNLOCK); - if (ret) - return ret; - } - - if (!info->ver.af || !af_lock) - return ret; - - if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_FOCUS) - ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP); - - return ret; -} - -static int m5mols_set_metering_mode(struct m5mols_info *info, int mode) -{ - unsigned int metering; - - switch (mode) { - case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: - metering = REG_AE_CENTER; - break; - case V4L2_EXPOSURE_METERING_SPOT: - metering = REG_AE_SPOT; - break; - default: - metering = REG_AE_ALL; - break; - } - - return m5mols_write(&info->sd, AE_MODE, metering); -} - -static int m5mols_set_exposure(struct m5mols_info *info, int exposure) -{ - struct v4l2_subdev *sd = &info->sd; - int ret = 0; - - if (exposure == V4L2_EXPOSURE_AUTO) { - /* Unlock auto exposure */ - info->lock_3a->val &= ~V4L2_LOCK_EXPOSURE; - m5mols_3a_lock(info, info->lock_3a); - - ret = m5mols_set_metering_mode(info, info->metering->val); - if (ret < 0) - return ret; - - v4l2_dbg(1, m5mols_debug, sd, - "%s: exposure bias: %#x, metering: %#x\n", - __func__, info->exposure_bias->val, - info->metering->val); - - return m5mols_write(sd, AE_INDEX, info->exposure_bias->val); - } - - if (exposure == V4L2_EXPOSURE_MANUAL) { - ret = m5mols_write(sd, AE_MODE, REG_AE_OFF); - if (ret == 0) - ret = m5mols_write(sd, AE_MAN_GAIN_MON, - info->exposure->val); - if (ret == 0) - ret = m5mols_write(sd, AE_MAN_GAIN_CAP, - info->exposure->val); - - v4l2_dbg(1, m5mols_debug, sd, "%s: exposure: %#x\n", - __func__, info->exposure->val); - } - - return ret; -} - -static int m5mols_set_white_balance(struct m5mols_info *info, int val) -{ - static const unsigned short wb[][2] = { - { V4L2_WHITE_BALANCE_INCANDESCENT, REG_AWB_INCANDESCENT }, - { V4L2_WHITE_BALANCE_FLUORESCENT, REG_AWB_FLUORESCENT_1 }, - { V4L2_WHITE_BALANCE_FLUORESCENT_H, REG_AWB_FLUORESCENT_2 }, - { V4L2_WHITE_BALANCE_HORIZON, REG_AWB_HORIZON }, - { V4L2_WHITE_BALANCE_DAYLIGHT, REG_AWB_DAYLIGHT }, - { V4L2_WHITE_BALANCE_FLASH, REG_AWB_LEDLIGHT }, - { V4L2_WHITE_BALANCE_CLOUDY, REG_AWB_CLOUDY }, - { V4L2_WHITE_BALANCE_SHADE, REG_AWB_SHADE }, - { V4L2_WHITE_BALANCE_AUTO, REG_AWB_AUTO }, - }; - int i; - struct v4l2_subdev *sd = &info->sd; - int ret = -EINVAL; - - for (i = 0; i < ARRAY_SIZE(wb); i++) { - int awb; - if (wb[i][0] != val) - continue; - - v4l2_dbg(1, m5mols_debug, sd, - "Setting white balance to: %#x\n", wb[i][0]); - - awb = wb[i][0] == V4L2_WHITE_BALANCE_AUTO; - ret = m5mols_write(sd, AWB_MODE, awb ? REG_AWB_AUTO : - REG_AWB_PRESET); - if (ret < 0) - return ret; - - if (!awb) - ret = m5mols_write(sd, AWB_MANUAL, wb[i][1]); - } - - return ret; -} - -static int m5mols_set_saturation(struct m5mols_info *info, int val) -{ - int ret = m5mols_write(&info->sd, MON_CHROMA_LVL, val); - if (ret < 0) - return ret; - - return m5mols_write(&info->sd, MON_CHROMA_EN, REG_CHROMA_ON); -} - -static int m5mols_set_color_effect(struct m5mols_info *info, int val) -{ - unsigned int m_effect = REG_COLOR_EFFECT_OFF; - unsigned int p_effect = REG_EFFECT_OFF; - unsigned int cfix_r = 0, cfix_b = 0; - struct v4l2_subdev *sd = &info->sd; - int ret = 0; - - switch (val) { - case V4L2_COLORFX_BW: - m_effect = REG_COLOR_EFFECT_ON; - break; - case V4L2_COLORFX_NEGATIVE: - p_effect = REG_EFFECT_NEGA; - break; - case V4L2_COLORFX_EMBOSS: - p_effect = REG_EFFECT_EMBOSS; - break; - case V4L2_COLORFX_SEPIA: - m_effect = REG_COLOR_EFFECT_ON; - cfix_r = REG_CFIXR_SEPIA; - cfix_b = REG_CFIXB_SEPIA; - break; - } - - ret = m5mols_write(sd, PARM_EFFECT, p_effect); - if (!ret) - ret = m5mols_write(sd, MON_EFFECT, m_effect); - - if (ret == 0 && m_effect == REG_COLOR_EFFECT_ON) { - ret = m5mols_write(sd, MON_CFIXR, cfix_r); - if (!ret) - ret = m5mols_write(sd, MON_CFIXB, cfix_b); - } - - v4l2_dbg(1, m5mols_debug, sd, - "p_effect: %#x, m_effect: %#x, r: %#x, b: %#x (%d)\n", - p_effect, m_effect, cfix_r, cfix_b, ret); - - return ret; -} - -static int m5mols_set_iso(struct m5mols_info *info, int auto_iso) -{ - u32 iso = auto_iso ? 0 : info->iso->val + 1; - - return m5mols_write(&info->sd, AE_ISO, iso); -} - -static int m5mols_set_wdr(struct m5mols_info *info, int wdr) -{ - int ret; - - ret = m5mols_write(&info->sd, MON_TONE_CTL, wdr ? 9 : 5); - if (ret < 0) - return ret; - - ret = m5mols_set_mode(info, REG_CAPTURE); - if (ret < 0) - return ret; - - return m5mols_write(&info->sd, CAPP_WDR_EN, wdr); -} - -static int m5mols_set_stabilization(struct m5mols_info *info, int val) -{ - struct v4l2_subdev *sd = &info->sd; - unsigned int evp = val ? 0xe : 0x0; - int ret; - - ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, evp); - if (ret < 0) - return ret; - - return m5mols_write(sd, AE_EV_PRESET_CAPTURE, evp); -} - -static int m5mols_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - struct m5mols_info *info = to_m5mols(sd); - int ret = 0; - u8 status; - - v4l2_dbg(1, m5mols_debug, sd, "%s: ctrl: %s (%d)\n", - __func__, ctrl->name, info->isp_ready); - - if (!info->isp_ready) - return -EBUSY; - - switch (ctrl->id) { - case V4L2_CID_ISO_SENSITIVITY_AUTO: - ret = m5mols_read_u8(sd, AE_ISO, &status); - if (ret == 0) - ctrl->val = !status; - if (status != REG_ISO_AUTO) - info->iso->val = status - 1; - break; - - case V4L2_CID_3A_LOCK: - ctrl->val &= ~0x7; - - ret = m5mols_read_u8(sd, AE_LOCK, &status); - if (ret) - return ret; - if (status) - info->lock_3a->val |= V4L2_LOCK_EXPOSURE; - - ret = m5mols_read_u8(sd, AWB_LOCK, &status); - if (ret) - return ret; - if (status) - info->lock_3a->val |= V4L2_LOCK_EXPOSURE; - - ret = m5mols_read_u8(sd, AF_EXECUTE, &status); - if (!status) - info->lock_3a->val |= V4L2_LOCK_EXPOSURE; - break; - } - - return ret; -} - -static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) -{ - unsigned int ctrl_mode = m5mols_get_ctrl_mode(ctrl); - struct v4l2_subdev *sd = to_sd(ctrl); - struct m5mols_info *info = to_m5mols(sd); - int last_mode = info->mode; - int ret = 0; - - /* - * If needed, defer restoring the controls until - * the device is fully initialized. - */ - if (!info->isp_ready) { - info->ctrl_sync = 0; - return 0; - } - - v4l2_dbg(1, m5mols_debug, sd, "%s: %s, val: %d, priv: %#x\n", - __func__, ctrl->name, ctrl->val, (int)ctrl->priv); - - if (ctrl_mode && ctrl_mode != info->mode) { - ret = m5mols_set_mode(info, ctrl_mode); - if (ret < 0) - return ret; - } - - switch (ctrl->id) { - case V4L2_CID_3A_LOCK: - ret = m5mols_3a_lock(info, ctrl); - break; - - case V4L2_CID_ZOOM_ABSOLUTE: - ret = m5mols_write(sd, MON_ZOOM, ctrl->val); - break; - - case V4L2_CID_EXPOSURE_AUTO: - ret = m5mols_set_exposure(info, ctrl->val); - break; - - case V4L2_CID_ISO_SENSITIVITY: - ret = m5mols_set_iso(info, ctrl->val); - break; - - case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: - ret = m5mols_set_white_balance(info, ctrl->val); - break; - - case V4L2_CID_SATURATION: - ret = m5mols_set_saturation(info, ctrl->val); - break; - - case V4L2_CID_COLORFX: - ret = m5mols_set_color_effect(info, ctrl->val); - break; - - case V4L2_CID_WIDE_DYNAMIC_RANGE: - ret = m5mols_set_wdr(info, ctrl->val); - break; - - case V4L2_CID_IMAGE_STABILIZATION: - ret = m5mols_set_stabilization(info, ctrl->val); - break; - - case V4L2_CID_JPEG_COMPRESSION_QUALITY: - ret = m5mols_write(sd, CAPP_JPEG_RATIO, ctrl->val); - break; - } - - if (ret == 0 && info->mode != last_mode) - ret = m5mols_set_mode(info, last_mode); - - return ret; -} - -static const struct v4l2_ctrl_ops m5mols_ctrl_ops = { - .g_volatile_ctrl = m5mols_g_volatile_ctrl, - .s_ctrl = m5mols_s_ctrl, -}; - -/* Supported manual ISO values */ -static const s64 iso_qmenu[] = { - /* AE_ISO: 0x01...0x07 (ISO: 50...3200) */ - 50000, 100000, 200000, 400000, 800000, 1600000, 3200000 -}; - -/* Supported Exposure Bias values, -2.0EV...+2.0EV */ -static const s64 ev_bias_qmenu[] = { - /* AE_INDEX: 0x00...0x08 */ - -2000, -1500, -1000, -500, 0, 500, 1000, 1500, 2000 -}; - -int m5mols_init_controls(struct v4l2_subdev *sd) -{ - struct m5mols_info *info = to_m5mols(sd); - u16 exposure_max; - u16 zoom_step; - int ret; - - /* Determine the firmware dependant control range and step values */ - ret = m5mols_read_u16(sd, AE_MAX_GAIN_MON, &exposure_max); - if (ret < 0) - return ret; - - zoom_step = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1; - v4l2_ctrl_handler_init(&info->handle, 20); - - info->auto_wb = v4l2_ctrl_new_std_menu(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, - 9, ~0x3fe, V4L2_WHITE_BALANCE_AUTO); - - /* Exposure control cluster */ - info->auto_exposure = v4l2_ctrl_new_std_menu(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, - 1, ~0x03, V4L2_EXPOSURE_AUTO); - - info->exposure = v4l2_ctrl_new_std(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_EXPOSURE, - 0, exposure_max, 1, exposure_max / 2); - - info->exposure_bias = v4l2_ctrl_new_int_menu(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_AUTO_EXPOSURE_BIAS, - ARRAY_SIZE(ev_bias_qmenu) - 1, - ARRAY_SIZE(ev_bias_qmenu)/2 - 1, - ev_bias_qmenu); - - info->metering = v4l2_ctrl_new_std_menu(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_METERING, - 2, ~0x7, V4L2_EXPOSURE_METERING_AVERAGE); - - /* ISO control cluster */ - info->auto_iso = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_ISO_SENSITIVITY_AUTO, 1, ~0x03, 1); - - info->iso = v4l2_ctrl_new_int_menu(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_ISO_SENSITIVITY, ARRAY_SIZE(iso_qmenu) - 1, - ARRAY_SIZE(iso_qmenu)/2 - 1, iso_qmenu); - - info->saturation = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_SATURATION, 1, 5, 1, 3); - - info->zoom = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_ZOOM_ABSOLUTE, 1, 70, zoom_step, 1); - - info->colorfx = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_COLORFX, 4, 0, V4L2_COLORFX_NONE); - - info->wdr = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_WIDE_DYNAMIC_RANGE, 0, 1, 1, 0); - - info->stabilization = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_IMAGE_STABILIZATION, 0, 1, 1, 0); - - info->jpeg_quality = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, 100, 1, 80); - - info->lock_3a = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_3A_LOCK, 0, 0x7, 0, 0); - - if (info->handle.error) { - int ret = info->handle.error; - v4l2_err(sd, "Failed to initialize controls: %d\n", ret); - v4l2_ctrl_handler_free(&info->handle); - return ret; - } - - v4l2_ctrl_auto_cluster(4, &info->auto_exposure, 1, false); - info->auto_iso->flags |= V4L2_CTRL_FLAG_VOLATILE | - V4L2_CTRL_FLAG_UPDATE; - v4l2_ctrl_auto_cluster(2, &info->auto_iso, 0, false); - - info->lock_3a->flags |= V4L2_CTRL_FLAG_VOLATILE; - - m5mols_set_ctrl_mode(info->auto_exposure, REG_PARAMETER); - m5mols_set_ctrl_mode(info->auto_wb, REG_PARAMETER); - m5mols_set_ctrl_mode(info->colorfx, REG_MONITOR); - - sd->ctrl_handler = &info->handle; - - return 0; -} diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c deleted file mode 100644 index ac7d28b6ddf..00000000000 --- a/drivers/media/video/m5mols/m5mols_core.c +++ /dev/null @@ -1,990 +0,0 @@ -/* - * Driver for M-5MOLS 8M Pixel camera sensor with ISP - * - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * Author: HeungJun Kim - * - * Copyright (C) 2009 Samsung Electronics Co., Ltd. - * Author: Dongsoo Nathaniel Kim - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "m5mols.h" -#include "m5mols_reg.h" - -int m5mols_debug; -module_param(m5mols_debug, int, 0644); - -#define MODULE_NAME "M5MOLS" -#define M5MOLS_I2C_CHECK_RETRY 500 - -/* The regulator consumer names for external voltage regulators */ -static struct regulator_bulk_data supplies[] = { - { - .supply = "core", /* ARM core power, 1.2V */ - }, { - .supply = "dig_18", /* digital power 1, 1.8V */ - }, { - .supply = "d_sensor", /* sensor power 1, 1.8V */ - }, { - .supply = "dig_28", /* digital power 2, 2.8V */ - }, { - .supply = "a_sensor", /* analog power */ - }, { - .supply = "dig_12", /* digital power 3, 1.2V */ - }, -}; - -static struct v4l2_mbus_framefmt m5mols_default_ffmt[M5MOLS_RESTYPE_MAX] = { - [M5MOLS_RESTYPE_MONITOR] = { - .width = 1920, - .height = 1080, - .code = V4L2_MBUS_FMT_VYUY8_2X8, - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_JPEG, - }, - [M5MOLS_RESTYPE_CAPTURE] = { - .width = 1920, - .height = 1080, - .code = V4L2_MBUS_FMT_JPEG_1X8, - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_JPEG, - }, -}; -#define SIZE_DEFAULT_FFMT ARRAY_SIZE(m5mols_default_ffmt) - -static const struct m5mols_resolution m5mols_reg_res[] = { - { 0x01, M5MOLS_RESTYPE_MONITOR, 128, 96 }, /* SUB-QCIF */ - { 0x03, M5MOLS_RESTYPE_MONITOR, 160, 120 }, /* QQVGA */ - { 0x05, M5MOLS_RESTYPE_MONITOR, 176, 144 }, /* QCIF */ - { 0x06, M5MOLS_RESTYPE_MONITOR, 176, 176 }, - { 0x08, M5MOLS_RESTYPE_MONITOR, 240, 320 }, /* QVGA */ - { 0x09, M5MOLS_RESTYPE_MONITOR, 320, 240 }, /* QVGA */ - { 0x0c, M5MOLS_RESTYPE_MONITOR, 240, 400 }, /* WQVGA */ - { 0x0d, M5MOLS_RESTYPE_MONITOR, 400, 240 }, /* WQVGA */ - { 0x0e, M5MOLS_RESTYPE_MONITOR, 352, 288 }, /* CIF */ - { 0x13, M5MOLS_RESTYPE_MONITOR, 480, 360 }, - { 0x15, M5MOLS_RESTYPE_MONITOR, 640, 360 }, /* qHD */ - { 0x17, M5MOLS_RESTYPE_MONITOR, 640, 480 }, /* VGA */ - { 0x18, M5MOLS_RESTYPE_MONITOR, 720, 480 }, - { 0x1a, M5MOLS_RESTYPE_MONITOR, 800, 480 }, /* WVGA */ - { 0x1f, M5MOLS_RESTYPE_MONITOR, 800, 600 }, /* SVGA */ - { 0x21, M5MOLS_RESTYPE_MONITOR, 1280, 720 }, /* HD */ - { 0x25, M5MOLS_RESTYPE_MONITOR, 1920, 1080 }, /* 1080p */ - { 0x29, M5MOLS_RESTYPE_MONITOR, 3264, 2448 }, /* 2.63fps 8M */ - { 0x39, M5MOLS_RESTYPE_MONITOR, 800, 602 }, /* AHS_MON debug */ - - { 0x02, M5MOLS_RESTYPE_CAPTURE, 320, 240 }, /* QVGA */ - { 0x04, M5MOLS_RESTYPE_CAPTURE, 400, 240 }, /* WQVGA */ - { 0x07, M5MOLS_RESTYPE_CAPTURE, 480, 360 }, - { 0x08, M5MOLS_RESTYPE_CAPTURE, 640, 360 }, /* qHD */ - { 0x09, M5MOLS_RESTYPE_CAPTURE, 640, 480 }, /* VGA */ - { 0x0a, M5MOLS_RESTYPE_CAPTURE, 800, 480 }, /* WVGA */ - { 0x10, M5MOLS_RESTYPE_CAPTURE, 1280, 720 }, /* HD */ - { 0x14, M5MOLS_RESTYPE_CAPTURE, 1280, 960 }, /* 1M */ - { 0x17, M5MOLS_RESTYPE_CAPTURE, 1600, 1200 }, /* 2M */ - { 0x19, M5MOLS_RESTYPE_CAPTURE, 1920, 1080 }, /* Full-HD */ - { 0x1a, M5MOLS_RESTYPE_CAPTURE, 2048, 1152 }, /* 3Mega */ - { 0x1b, M5MOLS_RESTYPE_CAPTURE, 2048, 1536 }, - { 0x1c, M5MOLS_RESTYPE_CAPTURE, 2560, 1440 }, /* 4Mega */ - { 0x1d, M5MOLS_RESTYPE_CAPTURE, 2560, 1536 }, - { 0x1f, M5MOLS_RESTYPE_CAPTURE, 2560, 1920 }, /* 5Mega */ - { 0x21, M5MOLS_RESTYPE_CAPTURE, 3264, 1836 }, /* 6Mega */ - { 0x22, M5MOLS_RESTYPE_CAPTURE, 3264, 1960 }, - { 0x25, M5MOLS_RESTYPE_CAPTURE, 3264, 2448 }, /* 8Mega */ -}; - -/** - * m5mols_swap_byte - an byte array to integer conversion function - * @size: size in bytes of I2C packet defined in the M-5MOLS datasheet - * - * Convert I2C data byte array with performing any required byte - * reordering to assure proper values for each data type, regardless - * of the architecture endianness. - */ -static u32 m5mols_swap_byte(u8 *data, u8 length) -{ - if (length == 1) - return *data; - else if (length == 2) - return be16_to_cpu(*((u16 *)data)); - else - return be32_to_cpu(*((u32 *)data)); -} - -/** - * m5mols_read - I2C read function - * @reg: combination of size, category and command for the I2C packet - * @size: desired size of I2C packet - * @val: read value - * - * Returns 0 on success, or else negative errno. - */ -static int m5mols_read(struct v4l2_subdev *sd, u32 size, u32 reg, u32 *val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct m5mols_info *info = to_m5mols(sd); - u8 rbuf[M5MOLS_I2C_MAX_SIZE + 1]; - u8 category = I2C_CATEGORY(reg); - u8 cmd = I2C_COMMAND(reg); - struct i2c_msg msg[2]; - u8 wbuf[5]; - int ret; - - if (!client->adapter) - return -ENODEV; - - msg[0].addr = client->addr; - msg[0].flags = 0; - msg[0].len = 5; - msg[0].buf = wbuf; - wbuf[0] = 5; - wbuf[1] = M5MOLS_BYTE_READ; - wbuf[2] = category; - wbuf[3] = cmd; - wbuf[4] = size; - - msg[1].addr = client->addr; - msg[1].flags = I2C_M_RD; - msg[1].len = size + 1; - msg[1].buf = rbuf; - - /* minimum stabilization time */ - usleep_range(200, 200); - - ret = i2c_transfer(client->adapter, msg, 2); - - if (ret == 2) { - *val = m5mols_swap_byte(&rbuf[1], size); - return 0; - } - - if (info->isp_ready) - v4l2_err(sd, "read failed: size:%d cat:%02x cmd:%02x. %d\n", - size, category, cmd, ret); - - return ret < 0 ? ret : -EIO; -} - -int m5mols_read_u8(struct v4l2_subdev *sd, u32 reg, u8 *val) -{ - u32 val_32; - int ret; - - if (I2C_SIZE(reg) != 1) { - v4l2_err(sd, "Wrong data size\n"); - return -EINVAL; - } - - ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32); - if (ret) - return ret; - - *val = (u8)val_32; - return ret; -} - -int m5mols_read_u16(struct v4l2_subdev *sd, u32 reg, u16 *val) -{ - u32 val_32; - int ret; - - if (I2C_SIZE(reg) != 2) { - v4l2_err(sd, "Wrong data size\n"); - return -EINVAL; - } - - ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32); - if (ret) - return ret; - - *val = (u16)val_32; - return ret; -} - -int m5mols_read_u32(struct v4l2_subdev *sd, u32 reg, u32 *val) -{ - if (I2C_SIZE(reg) != 4) { - v4l2_err(sd, "Wrong data size\n"); - return -EINVAL; - } - - return m5mols_read(sd, I2C_SIZE(reg), reg, val); -} - -/** - * m5mols_write - I2C command write function - * @reg: combination of size, category and command for the I2C packet - * @val: value to write - * - * Returns 0 on success, or else negative errno. - */ -int m5mols_write(struct v4l2_subdev *sd, u32 reg, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct m5mols_info *info = to_m5mols(sd); - u8 wbuf[M5MOLS_I2C_MAX_SIZE + 4]; - u8 category = I2C_CATEGORY(reg); - u8 cmd = I2C_COMMAND(reg); - u8 size = I2C_SIZE(reg); - u32 *buf = (u32 *)&wbuf[4]; - struct i2c_msg msg[1]; - int ret; - - if (!client->adapter) - return -ENODEV; - - if (size != 1 && size != 2 && size != 4) { - v4l2_err(sd, "Wrong data size\n"); - return -EINVAL; - } - - msg->addr = client->addr; - msg->flags = 0; - msg->len = (u16)size + 4; - msg->buf = wbuf; - wbuf[0] = size + 4; - wbuf[1] = M5MOLS_BYTE_WRITE; - wbuf[2] = category; - wbuf[3] = cmd; - - *buf = m5mols_swap_byte((u8 *)&val, size); - - usleep_range(200, 200); - - ret = i2c_transfer(client->adapter, msg, 1); - if (ret == 1) - return 0; - - if (info->isp_ready) - v4l2_err(sd, "write failed: cat:%02x cmd:%02x ret:%d\n", - category, cmd, ret); - - return ret < 0 ? ret : -EIO; -} - -/** - * m5mols_busy_wait - Busy waiting with I2C register polling - * @reg: the I2C_REG() address of an 8-bit status register to check - * @value: expected status register value - * @mask: bit mask for the read status register value - * @timeout: timeout in miliseconds, or -1 for default timeout - * - * The @reg register value is ORed with @mask before comparing with @value. - * - * Return: 0 if the requested condition became true within less than - * @timeout ms, or else negative errno. - */ -int m5mols_busy_wait(struct v4l2_subdev *sd, u32 reg, u32 value, u32 mask, - int timeout) -{ - int ms = timeout < 0 ? M5MOLS_BUSY_WAIT_DEF_TIMEOUT : timeout; - unsigned long end = jiffies + msecs_to_jiffies(ms); - u8 status; - - do { - int ret = m5mols_read_u8(sd, reg, &status); - - if (ret < 0 && !(mask & M5MOLS_I2C_RDY_WAIT_FL)) - return ret; - if (!ret && (status & mask & 0xff) == (value & 0xff)) - return 0; - usleep_range(100, 250); - } while (ms > 0 && time_is_after_jiffies(end)); - - return -EBUSY; -} - -/** - * m5mols_enable_interrupt - Clear interrupt pending bits and unmask interrupts - * - * Before writing desired interrupt value the INT_FACTOR register should - * be read to clear pending interrupts. - */ -int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg) -{ - struct m5mols_info *info = to_m5mols(sd); - u8 mask = is_available_af(info) ? REG_INT_AF : 0; - u8 dummy; - int ret; - - ret = m5mols_read_u8(sd, SYSTEM_INT_FACTOR, &dummy); - if (!ret) - ret = m5mols_write(sd, SYSTEM_INT_ENABLE, reg & ~mask); - return ret; -} - -int m5mols_wait_interrupt(struct v4l2_subdev *sd, u8 irq_mask, u32 timeout) -{ - struct m5mols_info *info = to_m5mols(sd); - - int ret = wait_event_interruptible_timeout(info->irq_waitq, - atomic_add_unless(&info->irq_done, -1, 0), - msecs_to_jiffies(timeout)); - if (ret <= 0) - return ret ? ret : -ETIMEDOUT; - - return m5mols_busy_wait(sd, SYSTEM_INT_FACTOR, irq_mask, - M5MOLS_I2C_RDY_WAIT_FL | irq_mask, -1); -} - -/** - * m5mols_reg_mode - Write the mode and check busy status - * - * It always accompanies a little delay changing the M-5MOLS mode, so it is - * needed checking current busy status to guarantee right mode. - */ -static int m5mols_reg_mode(struct v4l2_subdev *sd, u8 mode) -{ - int ret = m5mols_write(sd, SYSTEM_SYSMODE, mode); - if (ret < 0) - return ret; - return m5mols_busy_wait(sd, SYSTEM_SYSMODE, mode, 0xff, - M5MOLS_MODE_CHANGE_TIMEOUT); -} - -/** - * m5mols_set_mode - set the M-5MOLS controller mode - * @mode: the required operation mode - * - * The commands of M-5MOLS are grouped into specific modes. Each functionality - * can be guaranteed only when the sensor is operating in mode which a command - * belongs to. - */ -int m5mols_set_mode(struct m5mols_info *info, u8 mode) -{ - struct v4l2_subdev *sd = &info->sd; - int ret = -EINVAL; - u8 reg; - - if (mode < REG_PARAMETER || mode > REG_CAPTURE) - return ret; - - ret = m5mols_read_u8(sd, SYSTEM_SYSMODE, ®); - if (ret || reg == mode) - return ret; - - switch (reg) { - case REG_PARAMETER: - ret = m5mols_reg_mode(sd, REG_MONITOR); - if (mode == REG_MONITOR) - break; - if (!ret) - ret = m5mols_reg_mode(sd, REG_CAPTURE); - break; - - case REG_MONITOR: - if (mode == REG_PARAMETER) { - ret = m5mols_reg_mode(sd, REG_PARAMETER); - break; - } - - ret = m5mols_reg_mode(sd, REG_CAPTURE); - break; - - case REG_CAPTURE: - ret = m5mols_reg_mode(sd, REG_MONITOR); - if (mode == REG_MONITOR) - break; - if (!ret) - ret = m5mols_reg_mode(sd, REG_PARAMETER); - break; - - default: - v4l2_warn(sd, "Wrong mode: %d\n", mode); - } - - if (!ret) - info->mode = mode; - - return ret; -} - -/** - * m5mols_get_version - retrieve full revisions information of M-5MOLS - * - * The version information includes revisions of hardware and firmware, - * AutoFocus alghorithm version and the version string. - */ -static int m5mols_get_version(struct v4l2_subdev *sd) -{ - struct m5mols_info *info = to_m5mols(sd); - struct m5mols_version *ver = &info->ver; - u8 *str = ver->str; - int i; - int ret; - - ret = m5mols_read_u8(sd, SYSTEM_VER_CUSTOMER, &ver->customer); - if (!ret) - ret = m5mols_read_u8(sd, SYSTEM_VER_PROJECT, &ver->project); - if (!ret) - ret = m5mols_read_u16(sd, SYSTEM_VER_FIRMWARE, &ver->fw); - if (!ret) - ret = m5mols_read_u16(sd, SYSTEM_VER_HARDWARE, &ver->hw); - if (!ret) - ret = m5mols_read_u16(sd, SYSTEM_VER_PARAMETER, &ver->param); - if (!ret) - ret = m5mols_read_u16(sd, SYSTEM_VER_AWB, &ver->awb); - if (!ret) - ret = m5mols_read_u8(sd, AF_VERSION, &ver->af); - if (ret) - return ret; - - for (i = 0; i < VERSION_STRING_SIZE; i++) { - ret = m5mols_read_u8(sd, SYSTEM_VER_STRING, &str[i]); - if (ret) - return ret; - } - - ver->fw = be16_to_cpu(ver->fw); - ver->hw = be16_to_cpu(ver->hw); - ver->param = be16_to_cpu(ver->param); - ver->awb = be16_to_cpu(ver->awb); - - v4l2_info(sd, "Manufacturer\t[%s]\n", - is_manufacturer(info, REG_SAMSUNG_ELECTRO) ? - "Samsung Electro-Machanics" : - is_manufacturer(info, REG_SAMSUNG_OPTICS) ? - "Samsung Fiber-Optics" : - is_manufacturer(info, REG_SAMSUNG_TECHWIN) ? - "Samsung Techwin" : "None"); - v4l2_info(sd, "Customer/Project\t[0x%02x/0x%02x]\n", - info->ver.customer, info->ver.project); - - if (!is_available_af(info)) - v4l2_info(sd, "No support Auto Focus on this firmware\n"); - - return ret; -} - -/** - * __find_restype - Lookup M-5MOLS resolution type according to pixel code - * @code: pixel code - */ -static enum m5mols_restype __find_restype(enum v4l2_mbus_pixelcode code) -{ - enum m5mols_restype type = M5MOLS_RESTYPE_MONITOR; - - do { - if (code == m5mols_default_ffmt[type].code) - return type; - } while (type++ != SIZE_DEFAULT_FFMT); - - return 0; -} - -/** - * __find_resolution - Lookup preset and type of M-5MOLS's resolution - * @mf: pixel format to find/negotiate the resolution preset for - * @type: M-5MOLS resolution type - * @resolution: M-5MOLS resolution preset register value - * - * Find nearest resolution matching resolution preset and adjust mf - * to supported values. - */ -static int __find_resolution(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf, - enum m5mols_restype *type, - u32 *resolution) -{ - const struct m5mols_resolution *fsize = &m5mols_reg_res[0]; - const struct m5mols_resolution *match = NULL; - enum m5mols_restype stype = __find_restype(mf->code); - int i = ARRAY_SIZE(m5mols_reg_res); - unsigned int min_err = ~0; - - while (i--) { - int err; - if (stype == fsize->type) { - err = abs(fsize->width - mf->width) - + abs(fsize->height - mf->height); - - if (err < min_err) { - min_err = err; - match = fsize; - } - } - fsize++; - } - if (match) { - mf->width = match->width; - mf->height = match->height; - *resolution = match->reg; - *type = stype; - return 0; - } - - return -EINVAL; -} - -static struct v4l2_mbus_framefmt *__find_format(struct m5mols_info *info, - struct v4l2_subdev_fh *fh, - enum v4l2_subdev_format_whence which, - enum m5mols_restype type) -{ - if (which == V4L2_SUBDEV_FORMAT_TRY) - return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL; - - return &info->ffmt[type]; -} - -static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *fmt) -{ - struct m5mols_info *info = to_m5mols(sd); - struct v4l2_mbus_framefmt *format; - - format = __find_format(info, fh, fmt->which, info->res_type); - if (!format) - return -EINVAL; - - fmt->format = *format; - return 0; -} - -static int m5mols_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *fmt) -{ - struct m5mols_info *info = to_m5mols(sd); - struct v4l2_mbus_framefmt *format = &fmt->format; - struct v4l2_mbus_framefmt *sfmt; - enum m5mols_restype type; - u32 resolution = 0; - int ret; - - ret = __find_resolution(sd, format, &type, &resolution); - if (ret < 0) - return ret; - - sfmt = __find_format(info, fh, fmt->which, type); - if (!sfmt) - return 0; - - - format->code = m5mols_default_ffmt[type].code; - format->colorspace = V4L2_COLORSPACE_JPEG; - format->field = V4L2_FIELD_NONE; - - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - *sfmt = *format; - info->resolution = resolution; - info->res_type = type; - } - - return 0; -} - -static int m5mols_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (!code || code->index >= SIZE_DEFAULT_FFMT) - return -EINVAL; - - code->code = m5mols_default_ffmt[code->index].code; - - return 0; -} - -static struct v4l2_subdev_pad_ops m5mols_pad_ops = { - .enum_mbus_code = m5mols_enum_mbus_code, - .get_fmt = m5mols_get_fmt, - .set_fmt = m5mols_set_fmt, -}; - -/** - * m5mols_restore_controls - Apply current control values to the registers - * - * m5mols_do_scenemode() handles all parameters for which there is yet no - * individual control. It should be replaced at some point by setting each - * control individually, in required register set up order. - */ -int m5mols_restore_controls(struct m5mols_info *info) -{ - int ret; - - if (info->ctrl_sync) - return 0; - - ret = m5mols_do_scenemode(info, REG_SCENE_NORMAL); - if (ret) - return ret; - - ret = v4l2_ctrl_handler_setup(&info->handle); - info->ctrl_sync = !ret; - - return ret; -} - -/** - * m5mols_start_monitor - Start the monitor mode - * - * Before applying the controls setup the resolution and frame rate - * in PARAMETER mode, and then switch over to MONITOR mode. - */ -static int m5mols_start_monitor(struct m5mols_info *info) -{ - struct v4l2_subdev *sd = &info->sd; - int ret; - - ret = m5mols_set_mode(info, REG_PARAMETER); - if (!ret) - ret = m5mols_write(sd, PARM_MON_SIZE, info->resolution); - if (!ret) - ret = m5mols_write(sd, PARM_MON_FPS, REG_FPS_30); - if (!ret) - ret = m5mols_set_mode(info, REG_MONITOR); - if (!ret) - ret = m5mols_restore_controls(info); - - return ret; -} - -static int m5mols_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct m5mols_info *info = to_m5mols(sd); - u32 code = info->ffmt[info->res_type].code; - - if (enable) { - int ret = -EINVAL; - - if (is_code(code, M5MOLS_RESTYPE_MONITOR)) - ret = m5mols_start_monitor(info); - if (is_code(code, M5MOLS_RESTYPE_CAPTURE)) - ret = m5mols_start_capture(info); - - return ret; - } - - return m5mols_set_mode(info, REG_PARAMETER); -} - -static const struct v4l2_subdev_video_ops m5mols_video_ops = { - .s_stream = m5mols_s_stream, -}; - -static int m5mols_sensor_power(struct m5mols_info *info, bool enable) -{ - struct v4l2_subdev *sd = &info->sd; - struct i2c_client *client = v4l2_get_subdevdata(sd); - const struct m5mols_platform_data *pdata = info->pdata; - int ret; - - if (info->power == enable) - return 0; - - if (enable) { - if (info->set_power) { - ret = info->set_power(&client->dev, 1); - if (ret) - return ret; - } - - ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies); - if (ret) { - info->set_power(&client->dev, 0); - return ret; - } - - gpio_set_value(pdata->gpio_reset, !pdata->reset_polarity); - info->power = 1; - - return ret; - } - - ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies); - if (ret) - return ret; - - if (info->set_power) - info->set_power(&client->dev, 0); - - gpio_set_value(pdata->gpio_reset, pdata->reset_polarity); - - info->isp_ready = 0; - info->power = 0; - - return ret; -} - -/* m5mols_update_fw - optional firmware update routine */ -int __attribute__ ((weak)) m5mols_update_fw(struct v4l2_subdev *sd, - int (*set_power)(struct m5mols_info *, bool)) -{ - return 0; -} - -/** - * m5mols_fw_start - M-5MOLS internal ARM controller initialization - * - * Execute the M-5MOLS internal ARM controller initialization sequence. - * This function should be called after the supply voltage has been - * applied and before any requests to the device are made. - */ -static int m5mols_fw_start(struct v4l2_subdev *sd) -{ - struct m5mols_info *info = to_m5mols(sd); - int ret; - - atomic_set(&info->irq_done, 0); - /* Wait until I2C slave is initialized in Flash Writer mode */ - ret = m5mols_busy_wait(sd, FLASH_CAM_START, REG_IN_FLASH_MODE, - M5MOLS_I2C_RDY_WAIT_FL | 0xff, -1); - if (!ret) - ret = m5mols_write(sd, FLASH_CAM_START, REG_START_ARM_BOOT); - if (!ret) - ret = m5mols_wait_interrupt(sd, REG_INT_MODE, 2000); - if (ret < 0) - return ret; - - info->isp_ready = 1; - - ret = m5mols_get_version(sd); - if (!ret) - ret = m5mols_update_fw(sd, m5mols_sensor_power); - if (ret) - return ret; - - v4l2_dbg(1, m5mols_debug, sd, "Success ARM Booting\n"); - - ret = m5mols_write(sd, PARM_INTERFACE, REG_INTERFACE_MIPI); - if (!ret) - ret = m5mols_enable_interrupt(sd, - REG_INT_AF | REG_INT_CAPTURE); - - return ret; -} - -/** - * m5mols_s_power - Main sensor power control function - * - * To prevent breaking the lens when the sensor is powered off the Soft-Landing - * algorithm is called where available. The Soft-Landing algorithm availability - * dependends on the firmware provider. - */ -static int m5mols_s_power(struct v4l2_subdev *sd, int on) -{ - struct m5mols_info *info = to_m5mols(sd); - int ret; - - if (on) { - ret = m5mols_sensor_power(info, true); - if (!ret) - ret = m5mols_fw_start(sd); - return ret; - } - - if (is_manufacturer(info, REG_SAMSUNG_TECHWIN)) { - ret = m5mols_set_mode(info, REG_MONITOR); - if (!ret) - ret = m5mols_write(sd, AF_EXECUTE, REG_AF_STOP); - if (!ret) - ret = m5mols_write(sd, AF_MODE, REG_AF_POWEROFF); - if (!ret) - ret = m5mols_busy_wait(sd, SYSTEM_STATUS, REG_AF_IDLE, - 0xff, -1); - if (ret < 0) - v4l2_warn(sd, "Soft landing lens failed\n"); - } - - ret = m5mols_sensor_power(info, false); - info->ctrl_sync = 0; - - return ret; -} - -static int m5mols_log_status(struct v4l2_subdev *sd) -{ - struct m5mols_info *info = to_m5mols(sd); - - v4l2_ctrl_handler_log_status(&info->handle, sd->name); - - return 0; -} - -static const struct v4l2_subdev_core_ops m5mols_core_ops = { - .s_power = m5mols_s_power, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .log_status = m5mols_log_status, -}; - -/* - * V4L2 subdev internal operations - */ -static int m5mols_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); - - *format = m5mols_default_ffmt[0]; - return 0; -} - -static const struct v4l2_subdev_internal_ops m5mols_subdev_internal_ops = { - .open = m5mols_open, -}; - -static const struct v4l2_subdev_ops m5mols_ops = { - .core = &m5mols_core_ops, - .pad = &m5mols_pad_ops, - .video = &m5mols_video_ops, -}; - -static irqreturn_t m5mols_irq_handler(int irq, void *data) -{ - struct m5mols_info *info = to_m5mols(data); - - atomic_set(&info->irq_done, 1); - wake_up_interruptible(&info->irq_waitq); - - return IRQ_HANDLED; -} - -static int __devinit m5mols_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - const struct m5mols_platform_data *pdata = client->dev.platform_data; - struct m5mols_info *info; - struct v4l2_subdev *sd; - int ret; - - if (pdata == NULL) { - dev_err(&client->dev, "No platform data\n"); - return -EINVAL; - } - - if (!gpio_is_valid(pdata->gpio_reset)) { - dev_err(&client->dev, "No valid RESET GPIO specified\n"); - return -EINVAL; - } - - if (!client->irq) { - dev_err(&client->dev, "Interrupt not assigned\n"); - return -EINVAL; - } - - info = kzalloc(sizeof(struct m5mols_info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - info->pdata = pdata; - info->set_power = pdata->set_power; - - ret = gpio_request(pdata->gpio_reset, "M5MOLS_NRST"); - if (ret) { - dev_err(&client->dev, "Failed to request gpio: %d\n", ret); - goto out_free; - } - gpio_direction_output(pdata->gpio_reset, pdata->reset_polarity); - - ret = regulator_bulk_get(&client->dev, ARRAY_SIZE(supplies), supplies); - if (ret) { - dev_err(&client->dev, "Failed to get regulators: %d\n", ret); - goto out_gpio; - } - - sd = &info->sd; - v4l2_i2c_subdev_init(sd, client, &m5mols_ops); - strlcpy(sd->name, MODULE_NAME, sizeof(sd->name)); - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - sd->internal_ops = &m5mols_subdev_internal_ops; - info->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&sd->entity, 1, &info->pad, 0); - if (ret < 0) - goto out_reg; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - - init_waitqueue_head(&info->irq_waitq); - ret = request_irq(client->irq, m5mols_irq_handler, - IRQF_TRIGGER_RISING, MODULE_NAME, sd); - if (ret) { - dev_err(&client->dev, "Interrupt request failed: %d\n", ret); - goto out_me; - } - info->res_type = M5MOLS_RESTYPE_MONITOR; - info->ffmt[0] = m5mols_default_ffmt[0]; - info->ffmt[1] = m5mols_default_ffmt[1]; - - ret = m5mols_sensor_power(info, true); - if (ret) - goto out_me; - - ret = m5mols_fw_start(sd); - if (!ret) - ret = m5mols_init_controls(sd); - - m5mols_sensor_power(info, false); - if (!ret) - return 0; -out_me: - media_entity_cleanup(&sd->entity); -out_reg: - regulator_bulk_free(ARRAY_SIZE(supplies), supplies); -out_gpio: - gpio_free(pdata->gpio_reset); -out_free: - kfree(info); - return ret; -} - -static int __devexit m5mols_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct m5mols_info *info = to_m5mols(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(sd->ctrl_handler); - free_irq(client->irq, sd); - - regulator_bulk_free(ARRAY_SIZE(supplies), supplies); - gpio_free(info->pdata->gpio_reset); - media_entity_cleanup(&sd->entity); - kfree(info); - return 0; -} - -static const struct i2c_device_id m5mols_id[] = { - { MODULE_NAME, 0 }, - { }, -}; -MODULE_DEVICE_TABLE(i2c, m5mols_id); - -static struct i2c_driver m5mols_i2c_driver = { - .driver = { - .name = MODULE_NAME, - }, - .probe = m5mols_probe, - .remove = __devexit_p(m5mols_remove), - .id_table = m5mols_id, -}; - -module_i2c_driver(m5mols_i2c_driver); - -MODULE_AUTHOR("HeungJun Kim "); -MODULE_AUTHOR("Dongsoo Kim "); -MODULE_DESCRIPTION("Fujitsu M-5MOLS 8M Pixel camera driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/m5mols/m5mols_reg.h b/drivers/media/video/m5mols/m5mols_reg.h deleted file mode 100644 index 14d4be72aef..00000000000 --- a/drivers/media/video/m5mols/m5mols_reg.h +++ /dev/null @@ -1,362 +0,0 @@ -/* - * Register map for M-5MOLS 8M Pixel camera sensor with ISP - * - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * Author: HeungJun Kim - * - * Copyright (C) 2009 Samsung Electronics Co., Ltd. - * Author: Dongsoo Nathaniel Kim - * - * 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. - */ - -#ifndef M5MOLS_REG_H -#define M5MOLS_REG_H - -#define M5MOLS_I2C_MAX_SIZE 4 -#define M5MOLS_BYTE_READ 0x01 -#define M5MOLS_BYTE_WRITE 0x02 - -#define I2C_CATEGORY(__cat) ((__cat >> 16) & 0xff) -#define I2C_COMMAND(__comm) ((__comm >> 8) & 0xff) -#define I2C_SIZE(__reg_s) ((__reg_s) & 0xff) -#define I2C_REG(__cat, __cmd, __reg_s) ((__cat << 16) | (__cmd << 8) | __reg_s) - -/* - * Category section register - * - * The category means set including relevant command of M-5MOLS. - */ -#define CAT_SYSTEM 0x00 -#define CAT_PARAM 0x01 -#define CAT_MONITOR 0x02 -#define CAT_AE 0x03 -#define CAT_WB 0x06 -#define CAT_EXIF 0x07 -#define CAT_FD 0x09 -#define CAT_LENS 0x0a -#define CAT_CAPT_PARM 0x0b -#define CAT_CAPT_CTRL 0x0c -#define CAT_FLASH 0x0f /* related to FW, revisions, booting */ - -/* - * Category 0 - SYSTEM mode - * - * The SYSTEM mode in the M-5MOLS means area available to handle with the whole - * & all-round system of sensor. It deals with version/interrupt/setting mode & - * even sensor's status. Especially, the M-5MOLS sensor with ISP varies by - * packaging & manufacturer, even the customer and project code. And the - * function details may vary among them. The version information helps to - * determine what methods shall be used in the driver. - * - * There is many registers between customer version address and awb one. For - * more specific contents, see definition if file m5mols.h. - */ -#define SYSTEM_VER_CUSTOMER I2C_REG(CAT_SYSTEM, 0x00, 1) -#define SYSTEM_VER_PROJECT I2C_REG(CAT_SYSTEM, 0x01, 1) -#define SYSTEM_VER_FIRMWARE I2C_REG(CAT_SYSTEM, 0x02, 2) -#define SYSTEM_VER_HARDWARE I2C_REG(CAT_SYSTEM, 0x04, 2) -#define SYSTEM_VER_PARAMETER I2C_REG(CAT_SYSTEM, 0x06, 2) -#define SYSTEM_VER_AWB I2C_REG(CAT_SYSTEM, 0x08, 2) - -#define SYSTEM_SYSMODE I2C_REG(CAT_SYSTEM, 0x0b, 1) -#define REG_SYSINIT 0x00 /* SYSTEM mode */ -#define REG_PARAMETER 0x01 /* PARAMETER mode */ -#define REG_MONITOR 0x02 /* MONITOR mode */ -#define REG_CAPTURE 0x03 /* CAPTURE mode */ - -#define SYSTEM_CMD(__cmd) I2C_REG(CAT_SYSTEM, cmd, 1) -#define SYSTEM_VER_STRING I2C_REG(CAT_SYSTEM, 0x0a, 1) -#define REG_SAMSUNG_ELECTRO "SE" /* Samsung Electro-Mechanics */ -#define REG_SAMSUNG_OPTICS "OP" /* Samsung Fiber-Optics */ -#define REG_SAMSUNG_TECHWIN "TB" /* Samsung Techwin */ -/* SYSTEM mode status */ -#define SYSTEM_STATUS I2C_REG(CAT_SYSTEM, 0x0c, 1) - -/* Interrupt pending register */ -#define SYSTEM_INT_FACTOR I2C_REG(CAT_SYSTEM, 0x10, 1) -/* interrupt enable register */ -#define SYSTEM_INT_ENABLE I2C_REG(CAT_SYSTEM, 0x11, 1) -#define REG_INT_MODE (1 << 0) -#define REG_INT_AF (1 << 1) -#define REG_INT_ZOOM (1 << 2) -#define REG_INT_CAPTURE (1 << 3) -#define REG_INT_FRAMESYNC (1 << 4) -#define REG_INT_FD (1 << 5) -#define REG_INT_LENS_INIT (1 << 6) -#define REG_INT_SOUND (1 << 7) -#define REG_INT_MASK 0x0f - -/* - * category 1 - PARAMETER mode - * - * This category supports function of camera features of M-5MOLS. It means we - * can handle with preview(MONITOR) resolution size/frame per second/interface - * between the sensor and the Application Processor/even the image effect. - */ - -/* Resolution in the MONITOR mode */ -#define PARM_MON_SIZE I2C_REG(CAT_PARAM, 0x01, 1) - -/* Frame rate */ -#define PARM_MON_FPS I2C_REG(CAT_PARAM, 0x02, 1) -#define REG_FPS_30 0x02 - -/* Video bus between the sensor and a host processor */ -#define PARM_INTERFACE I2C_REG(CAT_PARAM, 0x00, 1) -#define REG_INTERFACE_MIPI 0x02 - -/* Image effects */ -#define PARM_EFFECT I2C_REG(CAT_PARAM, 0x0b, 1) -#define REG_EFFECT_OFF 0x00 -#define REG_EFFECT_NEGA 0x01 -#define REG_EFFECT_EMBOSS 0x06 -#define REG_EFFECT_OUTLINE 0x07 -#define REG_EFFECT_WATERCOLOR 0x08 - -/* - * Category 2 - MONITOR mode - * - * The MONITOR mode is same as preview mode as we said. The M-5MOLS has another - * mode named "Preview", but this preview mode is used at the case specific - * vider-recording mode. This mmode supports only YUYV format. On the other - * hand, the JPEG & RAW formats is supports by CAPTURE mode. And, there are - * another options like zoom/color effect(different with effect in PARAMETER - * mode)/anti hand shaking algorithm. - */ - -/* Target digital zoom position */ -#define MON_ZOOM I2C_REG(CAT_MONITOR, 0x01, 1) - -/* CR value for color effect */ -#define MON_CFIXR I2C_REG(CAT_MONITOR, 0x0a, 1) -/* CB value for color effect */ -#define MON_CFIXB I2C_REG(CAT_MONITOR, 0x09, 1) -#define REG_CFIXB_SEPIA 0xd8 -#define REG_CFIXR_SEPIA 0x18 - -#define MON_EFFECT I2C_REG(CAT_MONITOR, 0x0b, 1) -#define REG_COLOR_EFFECT_OFF 0x00 -#define REG_COLOR_EFFECT_ON 0x01 - -/* Chroma enable */ -#define MON_CHROMA_EN I2C_REG(CAT_MONITOR, 0x10, 1) -/* Chroma level */ -#define MON_CHROMA_LVL I2C_REG(CAT_MONITOR, 0x0f, 1) -#define REG_CHROMA_OFF 0x00 -#define REG_CHROMA_ON 0x01 - -/* Sharpness on/off */ -#define MON_EDGE_EN I2C_REG(CAT_MONITOR, 0x12, 1) -/* Sharpness level */ -#define MON_EDGE_LVL I2C_REG(CAT_MONITOR, 0x11, 1) -#define REG_EDGE_OFF 0x00 -#define REG_EDGE_ON 0x01 - -/* Set color tone (contrast) */ -#define MON_TONE_CTL I2C_REG(CAT_MONITOR, 0x25, 1) - -/* - * Category 3 - Auto Exposure - * - * The M-5MOLS exposure capbility is detailed as which is similar to digital - * camera. This category supports AE locking/various AE mode(range of exposure) - * /ISO/flickering/EV bias/shutter/meteoring, and anything else. And the - * maximum/minimum exposure gain value depending on M-5MOLS firmware, may be - * different. So, this category also provide getting the max/min values. And, - * each MONITOR and CAPTURE mode has each gain/shutter/max exposure values. - */ - -/* Auto Exposure locking */ -#define AE_LOCK I2C_REG(CAT_AE, 0x00, 1) -#define REG_AE_UNLOCK 0x00 -#define REG_AE_LOCK 0x01 - -/* Auto Exposure algorithm mode */ -#define AE_MODE I2C_REG(CAT_AE, 0x01, 1) -#define REG_AE_OFF 0x00 /* AE off */ -#define REG_AE_ALL 0x01 /* calc AE in all block integral */ -#define REG_AE_CENTER 0x03 /* calc AE in center weighted */ -#define REG_AE_SPOT 0x06 /* calc AE in specific spot */ - -#define AE_ISO I2C_REG(CAT_AE, 0x05, 1) -#define REG_ISO_AUTO 0x00 -#define REG_ISO_50 0x01 -#define REG_ISO_100 0x02 -#define REG_ISO_200 0x03 -#define REG_ISO_400 0x04 -#define REG_ISO_800 0x05 - -/* EV (scenemode) preset for MONITOR */ -#define AE_EV_PRESET_MONITOR I2C_REG(CAT_AE, 0x0a, 1) -/* EV (scenemode) preset for CAPTURE */ -#define AE_EV_PRESET_CAPTURE I2C_REG(CAT_AE, 0x0b, 1) -#define REG_SCENE_NORMAL 0x00 -#define REG_SCENE_PORTRAIT 0x01 -#define REG_SCENE_LANDSCAPE 0x02 -#define REG_SCENE_SPORTS 0x03 -#define REG_SCENE_PARTY_INDOOR 0x04 -#define REG_SCENE_BEACH_SNOW 0x05 -#define REG_SCENE_SUNSET 0x06 -#define REG_SCENE_DAWN_DUSK 0x07 -#define REG_SCENE_FALL 0x08 -#define REG_SCENE_NIGHT 0x09 -#define REG_SCENE_AGAINST_LIGHT 0x0a -#define REG_SCENE_FIRE 0x0b -#define REG_SCENE_TEXT 0x0c -#define REG_SCENE_CANDLE 0x0d - -/* Manual gain in MONITOR mode */ -#define AE_MAN_GAIN_MON I2C_REG(CAT_AE, 0x12, 2) -/* Maximum gain in MONITOR mode */ -#define AE_MAX_GAIN_MON I2C_REG(CAT_AE, 0x1a, 2) -/* Manual gain in CAPTURE mode */ -#define AE_MAN_GAIN_CAP I2C_REG(CAT_AE, 0x26, 2) - -#define AE_INDEX I2C_REG(CAT_AE, 0x38, 1) -#define REG_AE_INDEX_20_NEG 0x00 -#define REG_AE_INDEX_15_NEG 0x01 -#define REG_AE_INDEX_10_NEG 0x02 -#define REG_AE_INDEX_05_NEG 0x03 -#define REG_AE_INDEX_00 0x04 -#define REG_AE_INDEX_05_POS 0x05 -#define REG_AE_INDEX_10_POS 0x06 -#define REG_AE_INDEX_15_POS 0x07 -#define REG_AE_INDEX_20_POS 0x08 - -/* - * Category 6 - White Balance - */ - -/* Auto Whitebalance locking */ -#define AWB_LOCK I2C_REG(CAT_WB, 0x00, 1) -#define REG_AWB_UNLOCK 0x00 -#define REG_AWB_LOCK 0x01 - -#define AWB_MODE I2C_REG(CAT_WB, 0x02, 1) -#define REG_AWB_AUTO 0x01 /* AWB off */ -#define REG_AWB_PRESET 0x02 /* AWB preset */ - -/* Manual WB (preset) */ -#define AWB_MANUAL I2C_REG(CAT_WB, 0x03, 1) -#define REG_AWB_INCANDESCENT 0x01 -#define REG_AWB_FLUORESCENT_1 0x02 -#define REG_AWB_FLUORESCENT_2 0x03 -#define REG_AWB_DAYLIGHT 0x04 -#define REG_AWB_CLOUDY 0x05 -#define REG_AWB_SHADE 0x06 -#define REG_AWB_HORIZON 0x07 -#define REG_AWB_LEDLIGHT 0x09 - -/* - * Category 7 - EXIF information - */ -#define EXIF_INFO_EXPTIME_NU I2C_REG(CAT_EXIF, 0x00, 4) -#define EXIF_INFO_EXPTIME_DE I2C_REG(CAT_EXIF, 0x04, 4) -#define EXIF_INFO_TV_NU I2C_REG(CAT_EXIF, 0x08, 4) -#define EXIF_INFO_TV_DE I2C_REG(CAT_EXIF, 0x0c, 4) -#define EXIF_INFO_AV_NU I2C_REG(CAT_EXIF, 0x10, 4) -#define EXIF_INFO_AV_DE I2C_REG(CAT_EXIF, 0x14, 4) -#define EXIF_INFO_BV_NU I2C_REG(CAT_EXIF, 0x18, 4) -#define EXIF_INFO_BV_DE I2C_REG(CAT_EXIF, 0x1c, 4) -#define EXIF_INFO_EBV_NU I2C_REG(CAT_EXIF, 0x20, 4) -#define EXIF_INFO_EBV_DE I2C_REG(CAT_EXIF, 0x24, 4) -#define EXIF_INFO_ISO I2C_REG(CAT_EXIF, 0x28, 2) -#define EXIF_INFO_FLASH I2C_REG(CAT_EXIF, 0x2a, 2) -#define EXIF_INFO_SDR I2C_REG(CAT_EXIF, 0x2c, 2) -#define EXIF_INFO_QVAL I2C_REG(CAT_EXIF, 0x2e, 2) - -/* - * Category 9 - Face Detection - */ -#define FD_CTL I2C_REG(CAT_FD, 0x00, 1) -#define BIT_FD_EN 0 -#define BIT_FD_DRAW_FACE_FRAME 4 -#define BIT_FD_DRAW_SMILE_LVL 6 -#define REG_FD(shift) (1 << shift) -#define REG_FD_OFF 0x0 - -/* - * Category A - Lens Parameter - */ -#define AF_MODE I2C_REG(CAT_LENS, 0x01, 1) -#define REG_AF_NORMAL 0x00 /* Normal AF, one time */ -#define REG_AF_MACRO 0x01 /* Macro AF, one time */ -#define REG_AF_POWEROFF 0x07 - -#define AF_EXECUTE I2C_REG(CAT_LENS, 0x02, 1) -#define REG_AF_STOP 0x00 -#define REG_AF_EXE_AUTO 0x01 -#define REG_AF_EXE_CAF 0x02 - -#define AF_STATUS I2C_REG(CAT_LENS, 0x03, 1) -#define REG_AF_FAIL 0x00 -#define REG_AF_SUCCESS 0x02 -#define REG_AF_IDLE 0x04 -#define REG_AF_BUSY 0x05 - -#define AF_VERSION I2C_REG(CAT_LENS, 0x0a, 1) - -/* - * Category B - CAPTURE Parameter - */ -#define CAPP_YUVOUT_MAIN I2C_REG(CAT_CAPT_PARM, 0x00, 1) -#define REG_YUV422 0x00 -#define REG_BAYER10 0x05 -#define REG_BAYER8 0x06 -#define REG_JPEG 0x10 - -#define CAPP_MAIN_IMAGE_SIZE I2C_REG(CAT_CAPT_PARM, 0x01, 1) -#define CAPP_JPEG_RATIO I2C_REG(CAT_CAPT_PARM, 0x17, 1) - -#define CAPP_MCC_MODE I2C_REG(CAT_CAPT_PARM, 0x1d, 1) -#define REG_MCC_OFF 0x00 -#define REG_MCC_NORMAL 0x01 - -#define CAPP_WDR_EN I2C_REG(CAT_CAPT_PARM, 0x2c, 1) -#define REG_WDR_OFF 0x00 -#define REG_WDR_ON 0x01 -#define REG_WDR_AUTO 0x02 - -#define CAPP_LIGHT_CTRL I2C_REG(CAT_CAPT_PARM, 0x40, 1) -#define REG_LIGHT_OFF 0x00 -#define REG_LIGHT_ON 0x01 -#define REG_LIGHT_AUTO 0x02 - -#define CAPP_FLASH_CTRL I2C_REG(CAT_CAPT_PARM, 0x41, 1) -#define REG_FLASH_OFF 0x00 -#define REG_FLASH_ON 0x01 -#define REG_FLASH_AUTO 0x02 - -/* - * Category C - CAPTURE Control - */ -#define CAPC_MODE I2C_REG(CAT_CAPT_CTRL, 0x00, 1) -#define REG_CAP_NONE 0x00 -#define REG_CAP_ANTI_SHAKE 0x02 - -/* Select single- or multi-shot capture */ -#define CAPC_SEL_FRAME I2C_REG(CAT_CAPT_CTRL, 0x06, 1) - -#define CAPC_START I2C_REG(CAT_CAPT_CTRL, 0x09, 1) -#define REG_CAP_START_MAIN 0x01 -#define REG_CAP_START_THUMB 0x03 - -#define CAPC_IMAGE_SIZE I2C_REG(CAT_CAPT_CTRL, 0x0d, 4) -#define CAPC_THUMB_SIZE I2C_REG(CAT_CAPT_CTRL, 0x11, 4) - -/* - * Category F - Flash - * - * This mode provides functions about internal flash stuff and system startup. - */ - -/* Starts internal ARM core booting after power-up */ -#define FLASH_CAM_START I2C_REG(CAT_FLASH, 0x12, 1) -#define REG_START_ARM_BOOT 0x01 /* write value */ -#define REG_IN_FLASH_MODE 0x00 /* read value */ - -#endif /* M5MOLS_REG_H */ diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c deleted file mode 100644 index aeb22be7dcb..00000000000 --- a/drivers/media/video/msp3400-driver.c +++ /dev/null @@ -1,899 +0,0 @@ -/* - * Programming the mspx4xx sound processor family - * - * (c) 1997-2001 Gerd Knorr - * - * what works and what doesn't: - * - * AM-Mono - * Support for Hauppauge cards added (decoding handled by tuner) added by - * Frederic Crozat - * - * FM-Mono - * should work. The stereo modes are backward compatible to FM-mono, - * therefore FM-Mono should be allways available. - * - * FM-Stereo (B/G, used in germany) - * should work, with autodetect - * - * FM-Stereo (satellite) - * should work, no autodetect (i.e. default is mono, but you can - * switch to stereo -- untested) - * - * NICAM (B/G, L , used in UK, Scandinavia, Spain and France) - * should work, with autodetect. Support for NICAM was added by - * Pekka Pietikainen - * - * TODO: - * - better SAT support - * - * 980623 Thomas Sailer (sailer@ife.ee.ethz.ch) - * using soundcore instead of OSS - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "msp3400-driver.h" - -/* ---------------------------------------------------------------------- */ - -MODULE_DESCRIPTION("device driver for msp34xx TV sound processor"); -MODULE_AUTHOR("Gerd Knorr"); -MODULE_LICENSE("GPL"); - -/* module parameters */ -static int opmode = OPMODE_AUTO; -int msp_debug; /* msp_debug output */ -bool msp_once; /* no continuous stereo monitoring */ -bool msp_amsound; /* hard-wire AM sound at 6.5 Hz (france), - the autoscan seems work well only with FM... */ -int msp_standard = 1; /* Override auto detect of audio msp_standard, - if needed. */ -bool msp_dolby; - -int msp_stereo_thresh = 0x190; /* a2 threshold for stereo/bilingual - (msp34xxg only) 0x00a0-0x03c0 */ - -/* read-only */ -module_param(opmode, int, 0444); - -/* read-write */ -module_param_named(once, msp_once, bool, 0644); -module_param_named(debug, msp_debug, int, 0644); -module_param_named(stereo_threshold, msp_stereo_thresh, int, 0644); -module_param_named(standard, msp_standard, int, 0644); -module_param_named(amsound, msp_amsound, bool, 0644); -module_param_named(dolby, msp_dolby, bool, 0644); - -MODULE_PARM_DESC(opmode, "Forces a MSP3400 opmode. 0=Manual, 1=Autodetect, 2=Autodetect and autoselect"); -MODULE_PARM_DESC(once, "No continuous stereo monitoring"); -MODULE_PARM_DESC(debug, "Enable debug messages [0-3]"); -MODULE_PARM_DESC(stereo_threshold, "Sets signal threshold to activate stereo"); -MODULE_PARM_DESC(standard, "Specify audio standard: 32 = NTSC, 64 = radio, Default: Autodetect"); -MODULE_PARM_DESC(amsound, "Hardwire AM sound at 6.5Hz (France), FM can autoscan"); -MODULE_PARM_DESC(dolby, "Activates Dolby processing"); - -/* ---------------------------------------------------------------------- */ - -/* control subaddress */ -#define I2C_MSP_CONTROL 0x00 -/* demodulator unit subaddress */ -#define I2C_MSP_DEM 0x10 -/* DSP unit subaddress */ -#define I2C_MSP_DSP 0x12 - - -/* ----------------------------------------------------------------------- */ -/* functions for talking to the MSP3400C Sound processor */ - -int msp_reset(struct i2c_client *client) -{ - /* reset and read revision code */ - static u8 reset_off[3] = { I2C_MSP_CONTROL, 0x80, 0x00 }; - static u8 reset_on[3] = { I2C_MSP_CONTROL, 0x00, 0x00 }; - static u8 write[3] = { I2C_MSP_DSP + 1, 0x00, 0x1e }; - u8 read[2]; - struct i2c_msg reset[2] = { - { client->addr, I2C_M_IGNORE_NAK, 3, reset_off }, - { client->addr, I2C_M_IGNORE_NAK, 3, reset_on }, - }; - struct i2c_msg test[2] = { - { client->addr, 0, 3, write }, - { client->addr, I2C_M_RD, 2, read }, - }; - - v4l_dbg(3, msp_debug, client, "msp_reset\n"); - if (i2c_transfer(client->adapter, &reset[0], 1) != 1 || - i2c_transfer(client->adapter, &reset[1], 1) != 1 || - i2c_transfer(client->adapter, test, 2) != 2) { - v4l_err(client, "chip reset failed\n"); - return -1; - } - return 0; -} - -static int msp_read(struct i2c_client *client, int dev, int addr) -{ - int err, retval; - u8 write[3]; - u8 read[2]; - struct i2c_msg msgs[2] = { - { client->addr, 0, 3, write }, - { client->addr, I2C_M_RD, 2, read } - }; - - write[0] = dev + 1; - write[1] = addr >> 8; - write[2] = addr & 0xff; - - for (err = 0; err < 3; err++) { - if (i2c_transfer(client->adapter, msgs, 2) == 2) - break; - v4l_warn(client, "I/O error #%d (read 0x%02x/0x%02x)\n", err, - dev, addr); - schedule_timeout_interruptible(msecs_to_jiffies(10)); - } - if (err == 3) { - v4l_warn(client, "resetting chip, sound will go off.\n"); - msp_reset(client); - return -1; - } - retval = read[0] << 8 | read[1]; - v4l_dbg(3, msp_debug, client, "msp_read(0x%x, 0x%x): 0x%x\n", - dev, addr, retval); - return retval; -} - -int msp_read_dem(struct i2c_client *client, int addr) -{ - return msp_read(client, I2C_MSP_DEM, addr); -} - -int msp_read_dsp(struct i2c_client *client, int addr) -{ - return msp_read(client, I2C_MSP_DSP, addr); -} - -static int msp_write(struct i2c_client *client, int dev, int addr, int val) -{ - int err; - u8 buffer[5]; - - buffer[0] = dev; - buffer[1] = addr >> 8; - buffer[2] = addr & 0xff; - buffer[3] = val >> 8; - buffer[4] = val & 0xff; - - v4l_dbg(3, msp_debug, client, "msp_write(0x%x, 0x%x, 0x%x)\n", - dev, addr, val); - for (err = 0; err < 3; err++) { - if (i2c_master_send(client, buffer, 5) == 5) - break; - v4l_warn(client, "I/O error #%d (write 0x%02x/0x%02x)\n", err, - dev, addr); - schedule_timeout_interruptible(msecs_to_jiffies(10)); - } - if (err == 3) { - v4l_warn(client, "resetting chip, sound will go off.\n"); - msp_reset(client); - return -1; - } - return 0; -} - -int msp_write_dem(struct i2c_client *client, int addr, int val) -{ - return msp_write(client, I2C_MSP_DEM, addr, val); -} - -int msp_write_dsp(struct i2c_client *client, int addr, int val) -{ - return msp_write(client, I2C_MSP_DSP, addr, val); -} - -/* ----------------------------------------------------------------------- * - * bits 9 8 5 - SCART DSP input Select: - * 0 0 0 - SCART 1 to DSP input (reset position) - * 0 1 0 - MONO to DSP input - * 1 0 0 - SCART 2 to DSP input - * 1 1 1 - Mute DSP input - * - * bits 11 10 6 - SCART 1 Output Select: - * 0 0 0 - undefined (reset position) - * 0 1 0 - SCART 2 Input to SCART 1 Output (for devices with 2 SCARTS) - * 1 0 0 - MONO input to SCART 1 Output - * 1 1 0 - SCART 1 DA to SCART 1 Output - * 0 0 1 - SCART 2 DA to SCART 1 Output - * 0 1 1 - SCART 1 Input to SCART 1 Output - * 1 1 1 - Mute SCART 1 Output - * - * bits 13 12 7 - SCART 2 Output Select (for devices with 2 Output SCART): - * 0 0 0 - SCART 1 DA to SCART 2 Output (reset position) - * 0 1 0 - SCART 1 Input to SCART 2 Output - * 1 0 0 - MONO input to SCART 2 Output - * 0 0 1 - SCART 2 DA to SCART 2 Output - * 0 1 1 - SCART 2 Input to SCART 2 Output - * 1 1 0 - Mute SCART 2 Output - * - * Bits 4 to 0 should be zero. - * ----------------------------------------------------------------------- */ - -static int scarts[3][9] = { - /* MASK IN1 IN2 IN3 IN4 IN1_DA IN2_DA MONO MUTE */ - /* SCART DSP Input select */ - { 0x0320, 0x0000, 0x0200, 0x0300, 0x0020, -1, -1, 0x0100, 0x0320 }, - /* SCART1 Output select */ - { 0x0c40, 0x0440, 0x0400, 0x0000, 0x0840, 0x0c00, 0x0040, 0x0800, 0x0c40 }, - /* SCART2 Output select */ - { 0x3080, 0x1000, 0x1080, 0x2080, 0x3080, 0x0000, 0x0080, 0x2000, 0x3000 }, -}; - -static char *scart_names[] = { - "in1", "in2", "in3", "in4", "in1 da", "in2 da", "mono", "mute" -}; - -void msp_set_scart(struct i2c_client *client, int in, int out) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - - state->in_scart = in; - - if (in >= 0 && in <= 7 && out >= 0 && out <= 2) { - if (-1 == scarts[out][in + 1]) - return; - - state->acb &= ~scarts[out][0]; - state->acb |= scarts[out][in + 1]; - } else - state->acb = 0xf60; /* Mute Input and SCART 1 Output */ - - v4l_dbg(1, msp_debug, client, "scart switch: %s => %d (ACB=0x%04x)\n", - scart_names[in], out, state->acb); - msp_write_dsp(client, 0x13, state->acb); - - /* Sets I2S speed 0 = 1.024 Mbps, 1 = 2.048 Mbps */ - if (state->has_i2s_conf) - msp_write_dem(client, 0x40, state->i2s_mode); -} - -/* ------------------------------------------------------------------------ */ - -static void msp_wake_thread(struct i2c_client *client) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - - if (NULL == state->kthread) - return; - state->watch_stereo = 0; - state->restart = 1; - wake_up_interruptible(&state->wq); -} - -int msp_sleep(struct msp_state *state, int timeout) -{ - DECLARE_WAITQUEUE(wait, current); - - add_wait_queue(&state->wq, &wait); - if (!kthread_should_stop()) { - if (timeout < 0) { - set_current_state(TASK_INTERRUPTIBLE); - schedule(); - } else { - schedule_timeout_interruptible - (msecs_to_jiffies(timeout)); - } - } - - remove_wait_queue(&state->wq, &wait); - try_to_freeze(); - return state->restart; -} - -/* ------------------------------------------------------------------------ */ - -static int msp_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct msp_state *state = ctrl_to_state(ctrl); - struct i2c_client *client = v4l2_get_subdevdata(&state->sd); - int val = ctrl->val; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: { - /* audio volume cluster */ - int reallymuted = state->muted->val | state->scan_in_progress; - - if (!reallymuted) - val = (val * 0x7f / 65535) << 8; - - v4l_dbg(1, msp_debug, client, "mute=%s scanning=%s volume=%d\n", - state->muted->val ? "on" : "off", - state->scan_in_progress ? "yes" : "no", - state->volume->val); - - msp_write_dsp(client, 0x0000, val); - msp_write_dsp(client, 0x0007, reallymuted ? 0x1 : (val | 0x1)); - if (state->has_scart2_out_volume) - msp_write_dsp(client, 0x0040, reallymuted ? 0x1 : (val | 0x1)); - if (state->has_headphones) - msp_write_dsp(client, 0x0006, val); - break; - } - - case V4L2_CID_AUDIO_BASS: - val = ((val - 32768) * 0x60 / 65535) << 8; - msp_write_dsp(client, 0x0002, val); - if (state->has_headphones) - msp_write_dsp(client, 0x0031, val); - break; - - case V4L2_CID_AUDIO_TREBLE: - val = ((val - 32768) * 0x60 / 65535) << 8; - msp_write_dsp(client, 0x0003, val); - if (state->has_headphones) - msp_write_dsp(client, 0x0032, val); - break; - - case V4L2_CID_AUDIO_LOUDNESS: - val = val ? ((5 * 4) << 8) : 0; - msp_write_dsp(client, 0x0004, val); - if (state->has_headphones) - msp_write_dsp(client, 0x0033, val); - break; - - case V4L2_CID_AUDIO_BALANCE: - val = (u8)((val / 256) - 128); - msp_write_dsp(client, 0x0001, val << 8); - if (state->has_headphones) - msp_write_dsp(client, 0x0030, val << 8); - break; - - default: - return -EINVAL; - } - return 0; -} - -void msp_update_volume(struct msp_state *state) -{ - /* Force an update of the volume/mute cluster */ - v4l2_ctrl_lock(state->volume); - state->volume->val = state->volume->cur.val; - state->muted->val = state->muted->cur.val; - msp_s_ctrl(state->volume); - v4l2_ctrl_unlock(state->volume); -} - -/* --- v4l2 ioctls --- */ -static int msp_s_radio(struct v4l2_subdev *sd) -{ - struct msp_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (state->radio) - return 0; - state->radio = 1; - v4l_dbg(1, msp_debug, client, "switching to radio mode\n"); - state->watch_stereo = 0; - switch (state->opmode) { - case OPMODE_MANUAL: - /* set msp3400 to FM radio mode */ - msp3400c_set_mode(client, MSP_MODE_FM_RADIO); - msp3400c_set_carrier(client, MSP_CARRIER(10.7), - MSP_CARRIER(10.7)); - msp_update_volume(state); - break; - case OPMODE_AUTODETECT: - case OPMODE_AUTOSELECT: - /* the thread will do for us */ - msp_wake_thread(client); - break; - } - return 0; -} - -static int msp_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - /* new channel -- kick audio carrier scan */ - msp_wake_thread(client); - return 0; -} - -static int msp_querystd(struct v4l2_subdev *sd, v4l2_std_id *id) -{ - struct msp_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - *id &= state->detected_std; - - v4l_dbg(2, msp_debug, client, - "detected standard: %s(0x%08Lx)\n", - msp_standard_std_name(state->std), state->detected_std); - - return 0; -} - -static int msp_s_std(struct v4l2_subdev *sd, v4l2_std_id id) -{ - struct msp_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - int update = state->radio || state->v4l2_std != id; - - state->v4l2_std = id; - state->radio = 0; - if (update) - msp_wake_thread(client); - return 0; -} - -static int msp_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct msp_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - int tuner = (input >> 3) & 1; - int sc_in = input & 0x7; - int sc1_out = output & 0xf; - int sc2_out = (output >> 4) & 0xf; - u16 val, reg; - int i; - int extern_input = 1; - - if (state->route_in == input && state->route_out == output) - return 0; - state->route_in = input; - state->route_out = output; - /* check if the tuner input is used */ - for (i = 0; i < 5; i++) { - if (((input >> (4 + i * 4)) & 0xf) == 0) - extern_input = 0; - } - state->mode = extern_input ? MSP_MODE_EXTERN : MSP_MODE_AM_DETECT; - state->rxsubchans = V4L2_TUNER_SUB_STEREO; - msp_set_scart(client, sc_in, 0); - msp_set_scart(client, sc1_out, 1); - msp_set_scart(client, sc2_out, 2); - msp_set_audmode(client); - reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb; - val = msp_read_dem(client, reg); - msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8)); - /* wake thread when a new input is chosen */ - msp_wake_thread(client); - return 0; -} - -static int msp_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct msp_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (vt->type != V4L2_TUNER_ANALOG_TV) - return 0; - if (!state->radio) { - if (state->opmode == OPMODE_AUTOSELECT) - msp_detect_stereo(client); - vt->rxsubchans = state->rxsubchans; - } - vt->audmode = state->audmode; - vt->capability |= V4L2_TUNER_CAP_STEREO | - V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; - return 0; -} - -static int msp_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct msp_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (state->radio) /* TODO: add mono/stereo support for radio */ - return 0; - if (state->audmode == vt->audmode) - return 0; - state->audmode = vt->audmode; - /* only set audmode */ - msp_set_audmode(client); - return 0; -} - -static int msp_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq) -{ - struct msp_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - v4l_dbg(1, msp_debug, client, "Setting I2S speed to %d\n", freq); - - switch (freq) { - case 1024000: - state->i2s_mode = 0; - break; - case 2048000: - state->i2s_mode = 1; - break; - default: - return -EINVAL; - } - return 0; -} - -static int msp_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct msp_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, state->ident, - (state->rev1 << 16) | state->rev2); -} - -static int msp_log_status(struct v4l2_subdev *sd) -{ - struct msp_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - const char *p; - char prefix[V4L2_SUBDEV_NAME_SIZE + 20]; - - if (state->opmode == OPMODE_AUTOSELECT) - msp_detect_stereo(client); - v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n", - client->name, state->rev1, state->rev2); - snprintf(prefix, sizeof(prefix), "%s: Audio: ", sd->name); - v4l2_ctrl_handler_log_status(&state->hdl, prefix); - switch (state->mode) { - case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break; - case MSP_MODE_FM_RADIO: p = "FM Radio"; break; - case MSP_MODE_FM_TERRA: p = "Terrestrial FM-mono/stereo"; break; - case MSP_MODE_FM_SAT: p = "Satellite FM-mono"; break; - case MSP_MODE_FM_NICAM1: p = "NICAM/FM (B/G, D/K)"; break; - case MSP_MODE_FM_NICAM2: p = "NICAM/FM (I)"; break; - case MSP_MODE_AM_NICAM: p = "NICAM/AM (L)"; break; - case MSP_MODE_BTSC: p = "BTSC"; break; - case MSP_MODE_EXTERN: p = "External input"; break; - default: p = "unknown"; break; - } - if (state->mode == MSP_MODE_EXTERN) { - v4l_info(client, "Mode: %s\n", p); - } else if (state->opmode == OPMODE_MANUAL) { - v4l_info(client, "Mode: %s (%s%s)\n", p, - (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono", - (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : ""); - } else { - if (state->opmode == OPMODE_AUTODETECT) - v4l_info(client, "Mode: %s\n", p); - v4l_info(client, "Standard: %s (%s%s)\n", - msp_standard_std_name(state->std), - (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono", - (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : ""); - } - v4l_info(client, "Audmode: 0x%04x\n", state->audmode); - v4l_info(client, "Routing: 0x%08x (input) 0x%08x (output)\n", - state->route_in, state->route_out); - v4l_info(client, "ACB: 0x%04x\n", state->acb); - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int msp_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - v4l_dbg(1, msp_debug, client, "suspend\n"); - msp_reset(client); - return 0; -} - -static int msp_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - v4l_dbg(1, msp_debug, client, "resume\n"); - msp_wake_thread(client); - return 0; -} -#endif - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_ctrl_ops msp_ctrl_ops = { - .s_ctrl = msp_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops msp_core_ops = { - .log_status = msp_log_status, - .g_chip_ident = msp_g_chip_ident, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, - .s_std = msp_s_std, -}; - -static const struct v4l2_subdev_video_ops msp_video_ops = { - .querystd = msp_querystd, -}; - -static const struct v4l2_subdev_tuner_ops msp_tuner_ops = { - .s_frequency = msp_s_frequency, - .g_tuner = msp_g_tuner, - .s_tuner = msp_s_tuner, - .s_radio = msp_s_radio, -}; - -static const struct v4l2_subdev_audio_ops msp_audio_ops = { - .s_routing = msp_s_routing, - .s_i2s_clock_freq = msp_s_i2s_clock_freq, -}; - -static const struct v4l2_subdev_ops msp_ops = { - .core = &msp_core_ops, - .video = &msp_video_ops, - .tuner = &msp_tuner_ops, - .audio = &msp_audio_ops, -}; - -/* ----------------------------------------------------------------------- */ - -static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ - struct msp_state *state; - struct v4l2_subdev *sd; - struct v4l2_ctrl_handler *hdl; - int (*thread_func)(void *data) = NULL; - int msp_hard; - int msp_family; - int msp_revision; - int msp_product, msp_prod_hi, msp_prod_lo; - int msp_rom; - - if (!id) - strlcpy(client->name, "msp3400", sizeof(client->name)); - - if (msp_reset(client) == -1) { - v4l_dbg(1, msp_debug, client, "msp3400 not found\n"); - return -ENODEV; - } - - state = kzalloc(sizeof(*state), GFP_KERNEL); - if (!state) - return -ENOMEM; - - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &msp_ops); - - state->v4l2_std = V4L2_STD_NTSC; - state->detected_std = V4L2_STD_ALL; - state->audmode = V4L2_TUNER_MODE_STEREO; - state->input = -1; - state->i2s_mode = 0; - init_waitqueue_head(&state->wq); - /* These are the reset input/output positions */ - state->route_in = MSP_INPUT_DEFAULT; - state->route_out = MSP_OUTPUT_DEFAULT; - - state->rev1 = msp_read_dsp(client, 0x1e); - if (state->rev1 != -1) - state->rev2 = msp_read_dsp(client, 0x1f); - v4l_dbg(1, msp_debug, client, "rev1=0x%04x, rev2=0x%04x\n", - state->rev1, state->rev2); - if (state->rev1 == -1 || (state->rev1 == 0 && state->rev2 == 0)) { - v4l_dbg(1, msp_debug, client, - "not an msp3400 (cannot read chip version)\n"); - kfree(state); - return -ENODEV; - } - - msp_family = ((state->rev1 >> 4) & 0x0f) + 3; - msp_product = (state->rev2 >> 8) & 0xff; - msp_prod_hi = msp_product / 10; - msp_prod_lo = msp_product % 10; - msp_revision = (state->rev1 & 0x0f) + '@'; - msp_hard = ((state->rev1 >> 8) & 0xff) + '@'; - msp_rom = state->rev2 & 0x1f; - /* Rev B=2, C=3, D=4, G=7 */ - state->ident = msp_family * 10000 + 4000 + msp_product * 10 + - msp_revision - '@'; - - /* Has NICAM support: all mspx41x and mspx45x products have NICAM */ - state->has_nicam = - msp_prod_hi == 1 || msp_prod_hi == 5; - /* Has radio support: was added with revision G */ - state->has_radio = - msp_revision >= 'G'; - /* Has headphones output: not for stripped down products */ - state->has_headphones = - msp_prod_lo < 5; - /* Has scart2 input: not in stripped down products of the '3' family */ - state->has_scart2 = - msp_family >= 4 || msp_prod_lo < 7; - /* Has scart3 input: not in stripped down products of the '3' family */ - state->has_scart3 = - msp_family >= 4 || msp_prod_lo < 5; - /* Has scart4 input: not in pre D revisions, not in stripped D revs */ - state->has_scart4 = - msp_family >= 4 || (msp_revision >= 'D' && msp_prod_lo < 5); - /* Has scart2 output: not in stripped down products of - * the '3' family */ - state->has_scart2_out = - msp_family >= 4 || msp_prod_lo < 5; - /* Has scart2 a volume control? Not in pre-D revisions. */ - state->has_scart2_out_volume = - msp_revision > 'C' && state->has_scart2_out; - /* Has a configurable i2s out? */ - state->has_i2s_conf = - msp_revision >= 'G' && msp_prod_lo < 7; - /* Has subwoofer output: not in pre-D revs and not in stripped down - * products */ - state->has_subwoofer = - msp_revision >= 'D' && msp_prod_lo < 5; - /* Has soundprocessing (bass/treble/balance/loudness/equalizer): - * not in stripped down products */ - state->has_sound_processing = - msp_prod_lo < 7; - /* Has Virtual Dolby Surround: only in msp34x1 */ - state->has_virtual_dolby_surround = - msp_revision == 'G' && msp_prod_lo == 1; - /* Has Virtual Dolby Surround & Dolby Pro Logic: only in msp34x2 */ - state->has_dolby_pro_logic = - msp_revision == 'G' && msp_prod_lo == 2; - /* The msp343xG supports BTSC only and cannot do Automatic Standard - * Detection. */ - state->force_btsc = - msp_family == 3 && msp_revision == 'G' && msp_prod_hi == 3; - - state->opmode = opmode; - if (state->opmode == OPMODE_AUTO) { - /* MSP revision G and up have both autodetect and autoselect */ - if (msp_revision >= 'G') - state->opmode = OPMODE_AUTOSELECT; - /* MSP revision D and up have autodetect */ - else if (msp_revision >= 'D') - state->opmode = OPMODE_AUTODETECT; - else - state->opmode = OPMODE_MANUAL; - } - - hdl = &state->hdl; - v4l2_ctrl_handler_init(hdl, 6); - if (state->has_sound_processing) { - v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, - V4L2_CID_AUDIO_BASS, 0, 65535, 65535 / 100, 32768); - v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, - V4L2_CID_AUDIO_TREBLE, 0, 65535, 65535 / 100, 32768); - v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, - V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 0); - } - state->volume = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, - V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 58880); - v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, - V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768); - state->muted = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, - V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); - sd->ctrl_handler = hdl; - if (hdl->error) { - int err = hdl->error; - - v4l2_ctrl_handler_free(hdl); - kfree(state); - return err; - } - - v4l2_ctrl_cluster(2, &state->volume); - v4l2_ctrl_handler_setup(hdl); - - /* hello world :-) */ - v4l_info(client, "MSP%d4%02d%c-%c%d found @ 0x%x (%s)\n", - msp_family, msp_product, - msp_revision, msp_hard, msp_rom, - client->addr << 1, client->adapter->name); - v4l_info(client, "%s ", client->name); - if (state->has_nicam && state->has_radio) - printk(KERN_CONT "supports nicam and radio, "); - else if (state->has_nicam) - printk(KERN_CONT "supports nicam, "); - else if (state->has_radio) - printk(KERN_CONT "supports radio, "); - printk(KERN_CONT "mode is "); - - /* version-specific initialization */ - switch (state->opmode) { - case OPMODE_MANUAL: - printk(KERN_CONT "manual"); - thread_func = msp3400c_thread; - break; - case OPMODE_AUTODETECT: - printk(KERN_CONT "autodetect"); - thread_func = msp3410d_thread; - break; - case OPMODE_AUTOSELECT: - printk(KERN_CONT "autodetect and autoselect"); - thread_func = msp34xxg_thread; - break; - } - printk(KERN_CONT "\n"); - - /* startup control thread if needed */ - if (thread_func) { - state->kthread = kthread_run(thread_func, client, "msp34xx"); - - if (IS_ERR(state->kthread)) - v4l_warn(client, "kernel_thread() failed\n"); - msp_wake_thread(client); - } - return 0; -} - -static int msp_remove(struct i2c_client *client) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - - v4l2_device_unregister_subdev(&state->sd); - /* shutdown control thread */ - if (state->kthread) { - state->restart = 1; - kthread_stop(state->kthread); - } - msp_reset(client); - - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct dev_pm_ops msp3400_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(msp_suspend, msp_resume) -}; - -static const struct i2c_device_id msp_id[] = { - { "msp3400", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, msp_id); - -static struct i2c_driver msp_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "msp3400", - .pm = &msp3400_pm_ops, - }, - .probe = msp_probe, - .remove = msp_remove, - .id_table = msp_id, -}; - -module_i2c_driver(msp_driver); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/video/msp3400-driver.h b/drivers/media/video/msp3400-driver.h deleted file mode 100644 index fbe5e0715f9..00000000000 --- a/drivers/media/video/msp3400-driver.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - */ - -#ifndef MSP3400_DRIVER_H -#define MSP3400_DRIVER_H - -#include -#include -#include - -/* ---------------------------------------------------------------------- */ - -/* This macro is allowed for *constants* only, gcc must calculate it - at compile time. Remember -- no floats in kernel mode */ -#define MSP_CARRIER(freq) ((int)((float)(freq / 18.432) * (1 << 24))) - -#define MSP_MODE_AM_DETECT 0 -#define MSP_MODE_FM_RADIO 2 -#define MSP_MODE_FM_TERRA 3 -#define MSP_MODE_FM_SAT 4 -#define MSP_MODE_FM_NICAM1 5 -#define MSP_MODE_FM_NICAM2 6 -#define MSP_MODE_AM_NICAM 7 -#define MSP_MODE_BTSC 8 -#define MSP_MODE_EXTERN 9 - -#define SCART_IN1 0 -#define SCART_IN2 1 -#define SCART_IN3 2 -#define SCART_IN4 3 -#define SCART_IN1_DA 4 -#define SCART_IN2_DA 5 -#define SCART_MONO 6 -#define SCART_MUTE 7 - -#define SCART_DSP_IN 0 -#define SCART1_OUT 1 -#define SCART2_OUT 2 - -#define OPMODE_AUTO -1 -#define OPMODE_MANUAL 0 -#define OPMODE_AUTODETECT 1 /* use autodetect (>= msp3410 only) */ -#define OPMODE_AUTOSELECT 2 /* use autodetect & autoselect (>= msp34xxG) */ - -/* module parameters */ -extern int msp_debug; -extern bool msp_once; -extern bool msp_amsound; -extern int msp_standard; -extern bool msp_dolby; -extern int msp_stereo_thresh; - -struct msp_state { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - int rev1, rev2; - int ident; - u8 has_nicam; - u8 has_radio; - u8 has_headphones; - u8 has_ntsc_jp_d_k3; - u8 has_scart2; - u8 has_scart3; - u8 has_scart4; - u8 has_scart2_out; - u8 has_scart2_out_volume; - u8 has_i2s_conf; - u8 has_subwoofer; - u8 has_sound_processing; - u8 has_virtual_dolby_surround; - u8 has_dolby_pro_logic; - u8 force_btsc; - - int radio; - int opmode; - int std; - int mode; - v4l2_std_id v4l2_std, detected_std; - int nicam_on; - int acb; - int in_scart; - int i2s_mode; - int main, second; /* sound carrier */ - int input; - u32 route_in; - u32 route_out; - - /* v4l2 */ - int audmode; - int rxsubchans; - - struct { - /* volume cluster */ - struct v4l2_ctrl *volume; - struct v4l2_ctrl *muted; - }; - - int scan_in_progress; - - /* thread */ - struct task_struct *kthread; - wait_queue_head_t wq; - unsigned int restart:1; - unsigned int watch_stereo:1; -}; - -static inline struct msp_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct msp_state, sd); -} - -static inline struct msp_state *ctrl_to_state(struct v4l2_ctrl *ctrl) -{ - return container_of(ctrl->handler, struct msp_state, hdl); -} - -/* msp3400-driver.c */ -int msp_write_dem(struct i2c_client *client, int addr, int val); -int msp_write_dsp(struct i2c_client *client, int addr, int val); -int msp_read_dem(struct i2c_client *client, int addr); -int msp_read_dsp(struct i2c_client *client, int addr); -int msp_reset(struct i2c_client *client); -void msp_set_scart(struct i2c_client *client, int in, int out); -void msp_update_volume(struct msp_state *state); -int msp_sleep(struct msp_state *state, int timeout); - -/* msp3400-kthreads.c */ -const char *msp_standard_std_name(int std); -void msp_set_audmode(struct i2c_client *client); -int msp_detect_stereo(struct i2c_client *client); -int msp3400c_thread(void *data); -int msp3410d_thread(void *data); -int msp34xxg_thread(void *data); -void msp3400c_set_mode(struct i2c_client *client, int mode); -void msp3400c_set_carrier(struct i2c_client *client, int cdo1, int cdo2); - -#endif /* MSP3400_DRIVER_H */ diff --git a/drivers/media/video/msp3400-kthreads.c b/drivers/media/video/msp3400-kthreads.c deleted file mode 100644 index f8b51714f2f..00000000000 --- a/drivers/media/video/msp3400-kthreads.c +++ /dev/null @@ -1,1165 +0,0 @@ -/* - * Programming the mspx4xx sound processor family - * - * (c) 1997-2001 Gerd Knorr - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "msp3400-driver.h" - -/* this one uses the automatic sound standard detection of newer msp34xx - chip versions */ -static struct { - int retval; - int main, second; - char *name; - v4l2_std_id std; -} msp_stdlist[] = { - { 0x0000, 0, 0, "could not detect sound standard", V4L2_STD_ALL }, - { 0x0001, 0, 0, "autodetect start", V4L2_STD_ALL }, - { 0x0002, MSP_CARRIER(4.5), MSP_CARRIER(4.72), - "4.5/4.72 M Dual FM-Stereo", V4L2_STD_MN }, - { 0x0003, MSP_CARRIER(5.5), MSP_CARRIER(5.7421875), - "5.5/5.74 B/G Dual FM-Stereo", V4L2_STD_BG }, - { 0x0004, MSP_CARRIER(6.5), MSP_CARRIER(6.2578125), - "6.5/6.25 D/K1 Dual FM-Stereo", V4L2_STD_DK }, - { 0x0005, MSP_CARRIER(6.5), MSP_CARRIER(6.7421875), - "6.5/6.74 D/K2 Dual FM-Stereo", V4L2_STD_DK }, - { 0x0006, MSP_CARRIER(6.5), MSP_CARRIER(6.5), - "6.5 D/K FM-Mono (HDEV3)", V4L2_STD_DK }, - { 0x0007, MSP_CARRIER(6.5), MSP_CARRIER(5.7421875), - "6.5/5.74 D/K3 Dual FM-Stereo", V4L2_STD_DK }, - { 0x0008, MSP_CARRIER(5.5), MSP_CARRIER(5.85), - "5.5/5.85 B/G NICAM FM", V4L2_STD_BG }, - { 0x0009, MSP_CARRIER(6.5), MSP_CARRIER(5.85), - "6.5/5.85 L NICAM AM", V4L2_STD_L }, - { 0x000a, MSP_CARRIER(6.0), MSP_CARRIER(6.55), - "6.0/6.55 I NICAM FM", V4L2_STD_PAL_I }, - { 0x000b, MSP_CARRIER(6.5), MSP_CARRIER(5.85), - "6.5/5.85 D/K NICAM FM", V4L2_STD_DK }, - { 0x000c, MSP_CARRIER(6.5), MSP_CARRIER(5.85), - "6.5/5.85 D/K NICAM FM (HDEV2)", V4L2_STD_DK }, - { 0x000d, MSP_CARRIER(6.5), MSP_CARRIER(5.85), - "6.5/5.85 D/K NICAM FM (HDEV3)", V4L2_STD_DK }, - { 0x0020, MSP_CARRIER(4.5), MSP_CARRIER(4.5), - "4.5 M BTSC-Stereo", V4L2_STD_MTS }, - { 0x0021, MSP_CARRIER(4.5), MSP_CARRIER(4.5), - "4.5 M BTSC-Mono + SAP", V4L2_STD_MTS }, - { 0x0030, MSP_CARRIER(4.5), MSP_CARRIER(4.5), - "4.5 M EIA-J Japan Stereo", V4L2_STD_NTSC_M_JP }, - { 0x0040, MSP_CARRIER(10.7), MSP_CARRIER(10.7), - "10.7 FM-Stereo Radio", V4L2_STD_ALL }, - { 0x0050, MSP_CARRIER(6.5), MSP_CARRIER(6.5), - "6.5 SAT-Mono", V4L2_STD_ALL }, - { 0x0051, MSP_CARRIER(7.02), MSP_CARRIER(7.20), - "7.02/7.20 SAT-Stereo", V4L2_STD_ALL }, - { 0x0060, MSP_CARRIER(7.2), MSP_CARRIER(7.2), - "7.2 SAT ADR", V4L2_STD_ALL }, - { -1, 0, 0, NULL, 0 }, /* EOF */ -}; - -static struct msp3400c_init_data_dem { - int fir1[6]; - int fir2[6]; - int cdo1; - int cdo2; - int ad_cv; - int mode_reg; - int dsp_src; - int dsp_matrix; -} msp3400c_init_data[] = { - { /* AM (for carrier detect / msp3400) */ - {75, 19, 36, 35, 39, 40}, - {75, 19, 36, 35, 39, 40}, - MSP_CARRIER(5.5), MSP_CARRIER(5.5), - 0x00d0, 0x0500, 0x0020, 0x3000 - }, { /* AM (for carrier detect / msp3410) */ - {-1, -1, -8, 2, 59, 126}, - {-1, -1, -8, 2, 59, 126}, - MSP_CARRIER(5.5), MSP_CARRIER(5.5), - 0x00d0, 0x0100, 0x0020, 0x3000 - }, { /* FM Radio */ - {-8, -8, 4, 6, 78, 107}, - {-8, -8, 4, 6, 78, 107}, - MSP_CARRIER(10.7), MSP_CARRIER(10.7), - 0x00d0, 0x0480, 0x0020, 0x3000 - }, { /* Terrestrial FM-mono + FM-stereo */ - {3, 18, 27, 48, 66, 72}, - {3, 18, 27, 48, 66, 72}, - MSP_CARRIER(5.5), MSP_CARRIER(5.5), - 0x00d0, 0x0480, 0x0030, 0x3000 - }, { /* Sat FM-mono */ - { 1, 9, 14, 24, 33, 37}, - { 3, 18, 27, 48, 66, 72}, - MSP_CARRIER(6.5), MSP_CARRIER(6.5), - 0x00c6, 0x0480, 0x0000, 0x3000 - }, { /* NICAM/FM -- B/G (5.5/5.85), D/K (6.5/5.85) */ - {-2, -8, -10, 10, 50, 86}, - {3, 18, 27, 48, 66, 72}, - MSP_CARRIER(5.5), MSP_CARRIER(5.5), - 0x00d0, 0x0040, 0x0120, 0x3000 - }, { /* NICAM/FM -- I (6.0/6.552) */ - {2, 4, -6, -4, 40, 94}, - {3, 18, 27, 48, 66, 72}, - MSP_CARRIER(6.0), MSP_CARRIER(6.0), - 0x00d0, 0x0040, 0x0120, 0x3000 - }, { /* NICAM/AM -- L (6.5/5.85) */ - {-2, -8, -10, 10, 50, 86}, - {-4, -12, -9, 23, 79, 126}, - MSP_CARRIER(6.5), MSP_CARRIER(6.5), - 0x00c6, 0x0140, 0x0120, 0x7c00 - }, -}; - -struct msp3400c_carrier_detect { - int cdo; - char *name; -}; - -static struct msp3400c_carrier_detect msp3400c_carrier_detect_main[] = { - /* main carrier */ - { MSP_CARRIER(4.5), "4.5 NTSC" }, - { MSP_CARRIER(5.5), "5.5 PAL B/G" }, - { MSP_CARRIER(6.0), "6.0 PAL I" }, - { MSP_CARRIER(6.5), "6.5 PAL D/K + SAT + SECAM" } -}; - -static struct msp3400c_carrier_detect msp3400c_carrier_detect_55[] = { - /* PAL B/G */ - { MSP_CARRIER(5.7421875), "5.742 PAL B/G FM-stereo" }, - { MSP_CARRIER(5.85), "5.85 PAL B/G NICAM" } -}; - -static struct msp3400c_carrier_detect msp3400c_carrier_detect_65[] = { - /* PAL SAT / SECAM */ - { MSP_CARRIER(5.85), "5.85 PAL D/K + SECAM NICAM" }, - { MSP_CARRIER(6.2578125), "6.25 PAL D/K1 FM-stereo" }, - { MSP_CARRIER(6.7421875), "6.74 PAL D/K2 FM-stereo" }, - { MSP_CARRIER(7.02), "7.02 PAL SAT FM-stereo s/b" }, - { MSP_CARRIER(7.20), "7.20 PAL SAT FM-stereo s" }, - { MSP_CARRIER(7.38), "7.38 PAL SAT FM-stereo b" }, -}; - -/* ------------------------------------------------------------------------ */ - -const char *msp_standard_std_name(int std) -{ - int i; - - for (i = 0; msp_stdlist[i].name != NULL; i++) - if (msp_stdlist[i].retval == std) - return msp_stdlist[i].name; - return "unknown"; -} - -static v4l2_std_id msp_standard_std(int std) -{ - int i; - - for (i = 0; msp_stdlist[i].name != NULL; i++) - if (msp_stdlist[i].retval == std) - return msp_stdlist[i].std; - return V4L2_STD_ALL; -} - -static void msp_set_source(struct i2c_client *client, u16 src) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - - if (msp_dolby) { - msp_write_dsp(client, 0x0008, 0x0520); /* I2S1 */ - msp_write_dsp(client, 0x0009, 0x0620); /* I2S2 */ - } else { - msp_write_dsp(client, 0x0008, src); - msp_write_dsp(client, 0x0009, src); - } - msp_write_dsp(client, 0x000a, src); - msp_write_dsp(client, 0x000b, src); - msp_write_dsp(client, 0x000c, src); - if (state->has_scart2_out) - msp_write_dsp(client, 0x0041, src); -} - -void msp3400c_set_carrier(struct i2c_client *client, int cdo1, int cdo2) -{ - msp_write_dem(client, 0x0093, cdo1 & 0xfff); - msp_write_dem(client, 0x009b, cdo1 >> 12); - msp_write_dem(client, 0x00a3, cdo2 & 0xfff); - msp_write_dem(client, 0x00ab, cdo2 >> 12); - msp_write_dem(client, 0x0056, 0); /* LOAD_REG_1/2 */ -} - -void msp3400c_set_mode(struct i2c_client *client, int mode) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - struct msp3400c_init_data_dem *data = &msp3400c_init_data[mode]; - int tuner = (state->route_in >> 3) & 1; - int i; - - v4l_dbg(1, msp_debug, client, "set_mode: %d\n", mode); - state->mode = mode; - state->rxsubchans = V4L2_TUNER_SUB_MONO; - - msp_write_dem(client, 0x00bb, data->ad_cv | (tuner ? 0x100 : 0)); - - for (i = 5; i >= 0; i--) /* fir 1 */ - msp_write_dem(client, 0x0001, data->fir1[i]); - - msp_write_dem(client, 0x0005, 0x0004); /* fir 2 */ - msp_write_dem(client, 0x0005, 0x0040); - msp_write_dem(client, 0x0005, 0x0000); - for (i = 5; i >= 0; i--) - msp_write_dem(client, 0x0005, data->fir2[i]); - - msp_write_dem(client, 0x0083, data->mode_reg); - - msp3400c_set_carrier(client, data->cdo1, data->cdo2); - - msp_set_source(client, data->dsp_src); - /* set prescales */ - - /* volume prescale for SCART (AM mono input) */ - msp_write_dsp(client, 0x000d, 0x1900); - msp_write_dsp(client, 0x000e, data->dsp_matrix); - if (state->has_nicam) /* nicam prescale */ - msp_write_dsp(client, 0x0010, 0x5a00); -} - -/* Set audio mode. Note that the pre-'G' models do not support BTSC+SAP, - nor do they support stereo BTSC. */ -static void msp3400c_set_audmode(struct i2c_client *client) -{ - static char *strmode[] = { - "mono", "stereo", "lang2", "lang1", "lang1+lang2" - }; - struct msp_state *state = to_state(i2c_get_clientdata(client)); - char *modestr = (state->audmode >= 0 && state->audmode < 5) ? - strmode[state->audmode] : "unknown"; - int src = 0; /* channel source: FM/AM, nicam or SCART */ - int audmode = state->audmode; - - if (state->opmode == OPMODE_AUTOSELECT) { - /* this method would break everything, let's make sure - * it's never called - */ - v4l_dbg(1, msp_debug, client, - "set_audmode called with mode=%d instead of set_source (ignored)\n", - state->audmode); - return; - } - - /* Note: for the C and D revs no NTSC stereo + SAP is possible as - the hardware does not support SAP. So the rxsubchans combination - of STEREO | LANG2 does not occur. */ - - if (state->mode != MSP_MODE_EXTERN) { - /* switch to mono if only mono is available */ - if (state->rxsubchans == V4L2_TUNER_SUB_MONO) - audmode = V4L2_TUNER_MODE_MONO; - /* if bilingual */ - else if (state->rxsubchans & V4L2_TUNER_SUB_LANG2) { - /* and mono or stereo, then fallback to lang1 */ - if (audmode == V4L2_TUNER_MODE_MONO || - audmode == V4L2_TUNER_MODE_STEREO) - audmode = V4L2_TUNER_MODE_LANG1; - } - /* if stereo, and audmode is not mono, then switch to stereo */ - else if (audmode != V4L2_TUNER_MODE_MONO) - audmode = V4L2_TUNER_MODE_STEREO; - } - - /* switch demodulator */ - switch (state->mode) { - case MSP_MODE_FM_TERRA: - v4l_dbg(1, msp_debug, client, "FM set_audmode: %s\n", modestr); - switch (audmode) { - case V4L2_TUNER_MODE_STEREO: - msp_write_dsp(client, 0x000e, 0x3001); - break; - case V4L2_TUNER_MODE_MONO: - case V4L2_TUNER_MODE_LANG1: - case V4L2_TUNER_MODE_LANG2: - case V4L2_TUNER_MODE_LANG1_LANG2: - msp_write_dsp(client, 0x000e, 0x3000); - break; - } - break; - case MSP_MODE_FM_SAT: - v4l_dbg(1, msp_debug, client, "SAT set_audmode: %s\n", modestr); - switch (audmode) { - case V4L2_TUNER_MODE_MONO: - msp3400c_set_carrier(client, MSP_CARRIER(6.5), MSP_CARRIER(6.5)); - break; - case V4L2_TUNER_MODE_STEREO: - case V4L2_TUNER_MODE_LANG1_LANG2: - msp3400c_set_carrier(client, MSP_CARRIER(7.2), MSP_CARRIER(7.02)); - break; - case V4L2_TUNER_MODE_LANG1: - msp3400c_set_carrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); - break; - case V4L2_TUNER_MODE_LANG2: - msp3400c_set_carrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); - break; - } - break; - case MSP_MODE_FM_NICAM1: - case MSP_MODE_FM_NICAM2: - case MSP_MODE_AM_NICAM: - v4l_dbg(1, msp_debug, client, - "NICAM set_audmode: %s\n", modestr); - if (state->nicam_on) - src = 0x0100; /* NICAM */ - break; - case MSP_MODE_BTSC: - v4l_dbg(1, msp_debug, client, - "BTSC set_audmode: %s\n", modestr); - break; - case MSP_MODE_EXTERN: - v4l_dbg(1, msp_debug, client, - "extern set_audmode: %s\n", modestr); - src = 0x0200; /* SCART */ - break; - case MSP_MODE_FM_RADIO: - v4l_dbg(1, msp_debug, client, - "FM-Radio set_audmode: %s\n", modestr); - break; - default: - v4l_dbg(1, msp_debug, client, "mono set_audmode\n"); - return; - } - - /* switch audio */ - v4l_dbg(1, msp_debug, client, "set audmode %d\n", audmode); - switch (audmode) { - case V4L2_TUNER_MODE_STEREO: - case V4L2_TUNER_MODE_LANG1_LANG2: - src |= 0x0020; - break; - case V4L2_TUNER_MODE_MONO: - if (state->mode == MSP_MODE_AM_NICAM) { - v4l_dbg(1, msp_debug, client, "switching to AM mono\n"); - /* AM mono decoding is handled by tuner, not MSP chip */ - /* SCART switching control register */ - msp_set_scart(client, SCART_MONO, 0); - src = 0x0200; - break; - } - if (state->rxsubchans & V4L2_TUNER_SUB_STEREO) - src = 0x0030; - break; - case V4L2_TUNER_MODE_LANG1: - break; - case V4L2_TUNER_MODE_LANG2: - src |= 0x0010; - break; - } - v4l_dbg(1, msp_debug, client, - "set_audmode final source/matrix = 0x%x\n", src); - - msp_set_source(client, src); -} - -static void msp3400c_print_mode(struct i2c_client *client) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - - if (state->main == state->second) - v4l_dbg(1, msp_debug, client, - "mono sound carrier: %d.%03d MHz\n", - state->main / 910000, (state->main / 910) % 1000); - else - v4l_dbg(1, msp_debug, client, - "main sound carrier: %d.%03d MHz\n", - state->main / 910000, (state->main / 910) % 1000); - if (state->mode == MSP_MODE_FM_NICAM1 || state->mode == MSP_MODE_FM_NICAM2) - v4l_dbg(1, msp_debug, client, - "NICAM/FM carrier : %d.%03d MHz\n", - state->second / 910000, (state->second/910) % 1000); - if (state->mode == MSP_MODE_AM_NICAM) - v4l_dbg(1, msp_debug, client, - "NICAM/AM carrier : %d.%03d MHz\n", - state->second / 910000, (state->second / 910) % 1000); - if (state->mode == MSP_MODE_FM_TERRA && state->main != state->second) { - v4l_dbg(1, msp_debug, client, - "FM-stereo carrier : %d.%03d MHz\n", - state->second / 910000, (state->second / 910) % 1000); - } -} - -/* ----------------------------------------------------------------------- */ - -static int msp3400c_detect_stereo(struct i2c_client *client) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - int val; - int rxsubchans = state->rxsubchans; - int newnicam = state->nicam_on; - int update = 0; - - switch (state->mode) { - case MSP_MODE_FM_TERRA: - val = msp_read_dsp(client, 0x18); - if (val > 32767) - val -= 65536; - v4l_dbg(2, msp_debug, client, - "stereo detect register: %d\n", val); - if (val > 8192) { - rxsubchans = V4L2_TUNER_SUB_STEREO; - } else if (val < -4096) { - rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - } else { - rxsubchans = V4L2_TUNER_SUB_MONO; - } - newnicam = 0; - break; - case MSP_MODE_FM_NICAM1: - case MSP_MODE_FM_NICAM2: - case MSP_MODE_AM_NICAM: - val = msp_read_dem(client, 0x23); - v4l_dbg(2, msp_debug, client, "nicam sync=%d, mode=%d\n", - val & 1, (val & 0x1e) >> 1); - - if (val & 1) { - /* nicam synced */ - switch ((val & 0x1e) >> 1) { - case 0: - case 8: - rxsubchans = V4L2_TUNER_SUB_STEREO; - break; - case 1: - case 9: - rxsubchans = V4L2_TUNER_SUB_MONO; - break; - case 2: - case 10: - rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - break; - default: - rxsubchans = V4L2_TUNER_SUB_MONO; - break; - } - newnicam = 1; - } else { - newnicam = 0; - rxsubchans = V4L2_TUNER_SUB_MONO; - } - break; - } - if (rxsubchans != state->rxsubchans) { - update = 1; - v4l_dbg(1, msp_debug, client, - "watch: rxsubchans %02x => %02x\n", - state->rxsubchans, rxsubchans); - state->rxsubchans = rxsubchans; - } - if (newnicam != state->nicam_on) { - update = 1; - v4l_dbg(1, msp_debug, client, "watch: nicam %d => %d\n", - state->nicam_on, newnicam); - state->nicam_on = newnicam; - } - return update; -} - -/* - * A kernel thread for msp3400 control -- we don't want to block the - * in the ioctl while doing the sound carrier & stereo detect - */ -/* stereo/multilang monitoring */ -static void watch_stereo(struct i2c_client *client) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - - if (msp_detect_stereo(client)) - msp_set_audmode(client); - - if (msp_once) - state->watch_stereo = 0; -} - -int msp3400c_thread(void *data) -{ - struct i2c_client *client = data; - struct msp_state *state = to_state(i2c_get_clientdata(client)); - struct msp3400c_carrier_detect *cd; - int count, max1, max2, val1, val2, val, i; - - v4l_dbg(1, msp_debug, client, "msp3400 daemon started\n"); - state->detected_std = V4L2_STD_ALL; - set_freezable(); - for (;;) { - v4l_dbg(2, msp_debug, client, "msp3400 thread: sleep\n"); - msp_sleep(state, -1); - v4l_dbg(2, msp_debug, client, "msp3400 thread: wakeup\n"); - -restart: - v4l_dbg(2, msp_debug, client, "thread: restart scan\n"); - state->restart = 0; - if (kthread_should_stop()) - break; - - if (state->radio || MSP_MODE_EXTERN == state->mode) { - /* no carrier scan, just unmute */ - v4l_dbg(1, msp_debug, client, - "thread: no carrier scan\n"); - state->scan_in_progress = 0; - msp_update_volume(state); - continue; - } - - /* mute audio */ - state->scan_in_progress = 1; - msp_update_volume(state); - - msp3400c_set_mode(client, MSP_MODE_AM_DETECT); - val1 = val2 = 0; - max1 = max2 = -1; - state->watch_stereo = 0; - state->nicam_on = 0; - - /* wait for tuner to settle down after a channel change */ - if (msp_sleep(state, 200)) - goto restart; - - /* carrier detect pass #1 -- main carrier */ - cd = msp3400c_carrier_detect_main; - count = ARRAY_SIZE(msp3400c_carrier_detect_main); - - if (msp_amsound && (state->v4l2_std & V4L2_STD_SECAM)) { - /* autodetect doesn't work well with AM ... */ - max1 = 3; - count = 0; - v4l_dbg(1, msp_debug, client, "AM sound override\n"); - } - - for (i = 0; i < count; i++) { - msp3400c_set_carrier(client, cd[i].cdo, cd[i].cdo); - if (msp_sleep(state, 100)) - goto restart; - val = msp_read_dsp(client, 0x1b); - if (val > 32767) - val -= 65536; - if (val1 < val) - val1 = val, max1 = i; - v4l_dbg(1, msp_debug, client, - "carrier1 val: %5d / %s\n", val, cd[i].name); - } - - /* carrier detect pass #2 -- second (stereo) carrier */ - switch (max1) { - case 1: /* 5.5 */ - cd = msp3400c_carrier_detect_55; - count = ARRAY_SIZE(msp3400c_carrier_detect_55); - break; - case 3: /* 6.5 */ - cd = msp3400c_carrier_detect_65; - count = ARRAY_SIZE(msp3400c_carrier_detect_65); - break; - case 0: /* 4.5 */ - case 2: /* 6.0 */ - default: - cd = NULL; - count = 0; - break; - } - - if (msp_amsound && (state->v4l2_std & V4L2_STD_SECAM)) { - /* autodetect doesn't work well with AM ... */ - cd = NULL; - count = 0; - max2 = 0; - } - for (i = 0; i < count; i++) { - msp3400c_set_carrier(client, cd[i].cdo, cd[i].cdo); - if (msp_sleep(state, 100)) - goto restart; - val = msp_read_dsp(client, 0x1b); - if (val > 32767) - val -= 65536; - if (val2 < val) - val2 = val, max2 = i; - v4l_dbg(1, msp_debug, client, - "carrier2 val: %5d / %s\n", val, cd[i].name); - } - - /* program the msp3400 according to the results */ - state->main = msp3400c_carrier_detect_main[max1].cdo; - switch (max1) { - case 1: /* 5.5 */ - state->detected_std = V4L2_STD_BG | V4L2_STD_PAL_H; - if (max2 == 0) { - /* B/G FM-stereo */ - state->second = msp3400c_carrier_detect_55[max2].cdo; - msp3400c_set_mode(client, MSP_MODE_FM_TERRA); - state->watch_stereo = 1; - } else if (max2 == 1 && state->has_nicam) { - /* B/G NICAM */ - state->second = msp3400c_carrier_detect_55[max2].cdo; - msp3400c_set_mode(client, MSP_MODE_FM_NICAM1); - state->nicam_on = 1; - state->watch_stereo = 1; - } else { - goto no_second; - } - break; - case 2: /* 6.0 */ - /* PAL I NICAM */ - state->detected_std = V4L2_STD_PAL_I; - state->second = MSP_CARRIER(6.552); - msp3400c_set_mode(client, MSP_MODE_FM_NICAM2); - state->nicam_on = 1; - state->watch_stereo = 1; - break; - case 3: /* 6.5 */ - if (max2 == 1 || max2 == 2) { - /* D/K FM-stereo */ - state->second = msp3400c_carrier_detect_65[max2].cdo; - msp3400c_set_mode(client, MSP_MODE_FM_TERRA); - state->watch_stereo = 1; - state->detected_std = V4L2_STD_DK; - } else if (max2 == 0 && (state->v4l2_std & V4L2_STD_SECAM)) { - /* L NICAM or AM-mono */ - state->second = msp3400c_carrier_detect_65[max2].cdo; - msp3400c_set_mode(client, MSP_MODE_AM_NICAM); - state->watch_stereo = 1; - state->detected_std = V4L2_STD_L; - } else if (max2 == 0 && state->has_nicam) { - /* D/K NICAM */ - state->second = msp3400c_carrier_detect_65[max2].cdo; - msp3400c_set_mode(client, MSP_MODE_FM_NICAM1); - state->nicam_on = 1; - state->watch_stereo = 1; - state->detected_std = V4L2_STD_DK; - } else { - goto no_second; - } - break; - case 0: /* 4.5 */ - state->detected_std = V4L2_STD_MN; - default: -no_second: - state->second = msp3400c_carrier_detect_main[max1].cdo; - msp3400c_set_mode(client, MSP_MODE_FM_TERRA); - break; - } - msp3400c_set_carrier(client, state->second, state->main); - - /* unmute */ - state->scan_in_progress = 0; - msp3400c_set_audmode(client); - msp_update_volume(state); - - if (msp_debug) - msp3400c_print_mode(client); - - /* monitor tv audio mode, the first time don't wait - so long to get a quick stereo/bilingual result */ - count = 3; - while (state->watch_stereo) { - if (msp_sleep(state, count ? 1000 : 5000)) - goto restart; - if (count) - count--; - watch_stereo(client); - } - } - v4l_dbg(1, msp_debug, client, "thread: exit\n"); - return 0; -} - - -int msp3410d_thread(void *data) -{ - struct i2c_client *client = data; - struct msp_state *state = to_state(i2c_get_clientdata(client)); - int val, i, std, count; - - v4l_dbg(1, msp_debug, client, "msp3410 daemon started\n"); - state->detected_std = V4L2_STD_ALL; - set_freezable(); - for (;;) { - v4l_dbg(2, msp_debug, client, "msp3410 thread: sleep\n"); - msp_sleep(state, -1); - v4l_dbg(2, msp_debug, client, "msp3410 thread: wakeup\n"); - -restart: - v4l_dbg(2, msp_debug, client, "thread: restart scan\n"); - state->restart = 0; - if (kthread_should_stop()) - break; - - if (state->mode == MSP_MODE_EXTERN) { - /* no carrier scan needed, just unmute */ - v4l_dbg(1, msp_debug, client, - "thread: no carrier scan\n"); - state->scan_in_progress = 0; - msp_update_volume(state); - continue; - } - - /* mute audio */ - state->scan_in_progress = 1; - msp_update_volume(state); - - /* start autodetect. Note: autodetect is not supported for - NTSC-M and radio, hence we force the standard in those - cases. */ - if (state->radio) - std = 0x40; - else - std = (state->v4l2_std & V4L2_STD_NTSC) ? 0x20 : 1; - state->watch_stereo = 0; - state->nicam_on = 0; - - /* wait for tuner to settle down after a channel change */ - if (msp_sleep(state, 200)) - goto restart; - - if (msp_debug) - v4l_dbg(2, msp_debug, client, - "setting standard: %s (0x%04x)\n", - msp_standard_std_name(std), std); - - if (std != 1) { - /* programmed some specific mode */ - val = std; - } else { - /* triggered autodetect */ - msp_write_dem(client, 0x20, std); - for (;;) { - if (msp_sleep(state, 100)) - goto restart; - - /* check results */ - val = msp_read_dem(client, 0x7e); - if (val < 0x07ff) - break; - v4l_dbg(2, msp_debug, client, - "detection still in progress\n"); - } - } - for (i = 0; msp_stdlist[i].name != NULL; i++) - if (msp_stdlist[i].retval == val) - break; - v4l_dbg(1, msp_debug, client, "current standard: %s (0x%04x)\n", - msp_standard_std_name(val), val); - state->main = msp_stdlist[i].main; - state->second = msp_stdlist[i].second; - state->std = val; - state->rxsubchans = V4L2_TUNER_SUB_MONO; - - if (msp_amsound && !state->radio && - (state->v4l2_std & V4L2_STD_SECAM) && (val != 0x0009)) { - /* autodetection has failed, let backup */ - v4l_dbg(1, msp_debug, client, "autodetection failed," - " switching to backup standard: %s (0x%04x)\n", - msp_stdlist[8].name ? - msp_stdlist[8].name : "unknown", val); - state->std = val = 0x0009; - msp_write_dem(client, 0x20, val); - } else { - state->detected_std = msp_standard_std(state->std); - } - - /* set stereo */ - switch (val) { - case 0x0008: /* B/G NICAM */ - case 0x000a: /* I NICAM */ - case 0x000b: /* D/K NICAM */ - if (val == 0x000a) - state->mode = MSP_MODE_FM_NICAM2; - else - state->mode = MSP_MODE_FM_NICAM1; - /* just turn on stereo */ - state->nicam_on = 1; - state->watch_stereo = 1; - break; - case 0x0009: - state->mode = MSP_MODE_AM_NICAM; - state->nicam_on = 1; - state->watch_stereo = 1; - break; - case 0x0020: /* BTSC */ - /* The pre-'G' models only have BTSC-mono */ - state->mode = MSP_MODE_BTSC; - break; - case 0x0040: /* FM radio */ - state->mode = MSP_MODE_FM_RADIO; - state->rxsubchans = V4L2_TUNER_SUB_STEREO; - /* not needed in theory if we have radio, but - short programming enables carrier mute */ - msp3400c_set_mode(client, MSP_MODE_FM_RADIO); - msp3400c_set_carrier(client, MSP_CARRIER(10.7), - MSP_CARRIER(10.7)); - break; - case 0x0002: - case 0x0003: - case 0x0004: - case 0x0005: - state->mode = MSP_MODE_FM_TERRA; - state->watch_stereo = 1; - break; - } - - /* set various prescales */ - msp_write_dsp(client, 0x0d, 0x1900); /* scart */ - msp_write_dsp(client, 0x0e, 0x3000); /* FM */ - if (state->has_nicam) - msp_write_dsp(client, 0x10, 0x5a00); /* nicam */ - - if (state->has_i2s_conf) - msp_write_dem(client, 0x40, state->i2s_mode); - - /* unmute */ - msp3400c_set_audmode(client); - state->scan_in_progress = 0; - msp_update_volume(state); - - /* monitor tv audio mode, the first time don't wait - so long to get a quick stereo/bilingual result */ - count = 3; - while (state->watch_stereo) { - if (msp_sleep(state, count ? 1000 : 5000)) - goto restart; - if (count) - count--; - watch_stereo(client); - } - } - v4l_dbg(1, msp_debug, client, "thread: exit\n"); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -/* msp34xxG + (autoselect no-thread) - * this one uses both automatic standard detection and automatic sound - * select which are available in the newer G versions - * struct msp: only norm, acb and source are really used in this mode - */ - -static int msp34xxg_modus(struct i2c_client *client) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - - if (state->radio) { - v4l_dbg(1, msp_debug, client, "selected radio modus\n"); - return 0x0001; - } - if (state->v4l2_std == V4L2_STD_NTSC_M_JP) { - v4l_dbg(1, msp_debug, client, "selected M (EIA-J) modus\n"); - return 0x4001; - } - if (state->v4l2_std == V4L2_STD_NTSC_M_KR) { - v4l_dbg(1, msp_debug, client, "selected M (A2) modus\n"); - return 0x0001; - } - if (state->v4l2_std == V4L2_STD_SECAM_L) { - v4l_dbg(1, msp_debug, client, "selected SECAM-L modus\n"); - return 0x6001; - } - if (state->v4l2_std & V4L2_STD_MN) { - v4l_dbg(1, msp_debug, client, "selected M (BTSC) modus\n"); - return 0x2001; - } - return 0x7001; -} - -static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in) - { - struct msp_state *state = to_state(i2c_get_clientdata(client)); - int source, matrix; - - switch (state->audmode) { - case V4L2_TUNER_MODE_MONO: - source = 0; /* mono only */ - matrix = 0x30; - break; - case V4L2_TUNER_MODE_LANG2: - source = 4; /* stereo or B */ - matrix = 0x10; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - source = 1; /* stereo or A|B */ - matrix = 0x20; - break; - case V4L2_TUNER_MODE_LANG1: - source = 3; /* stereo or A */ - matrix = 0x00; - break; - case V4L2_TUNER_MODE_STEREO: - default: - source = 3; /* stereo or A */ - matrix = 0x20; - break; - } - - if (in == MSP_DSP_IN_TUNER) - source = (source << 8) | 0x20; - /* the msp34x2g puts the MAIN_AVC, MAIN and AUX sources in 12, 13, 14 - instead of 11, 12, 13. So we add one for that msp version. */ - else if (in >= MSP_DSP_IN_MAIN_AVC && state->has_dolby_pro_logic) - source = ((in + 1) << 8) | matrix; - else - source = (in << 8) | matrix; - - v4l_dbg(1, msp_debug, client, - "set source to %d (0x%x) for output %02x\n", in, source, reg); - msp_write_dsp(client, reg, source); -} - -static void msp34xxg_set_sources(struct i2c_client *client) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - u32 in = state->route_in; - - msp34xxg_set_source(client, 0x0008, (in >> 4) & 0xf); - /* quasi-peak detector is set to same input as the loudspeaker (MAIN) */ - msp34xxg_set_source(client, 0x000c, (in >> 4) & 0xf); - msp34xxg_set_source(client, 0x0009, (in >> 8) & 0xf); - msp34xxg_set_source(client, 0x000a, (in >> 12) & 0xf); - if (state->has_scart2_out) - msp34xxg_set_source(client, 0x0041, (in >> 16) & 0xf); - msp34xxg_set_source(client, 0x000b, (in >> 20) & 0xf); -} - -/* (re-)initialize the msp34xxg */ -static void msp34xxg_reset(struct i2c_client *client) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - int tuner = (state->route_in >> 3) & 1; - int modus; - - /* initialize std to 1 (autodetect) to signal that no standard is - selected yet. */ - state->std = 1; - - msp_reset(client); - - if (state->has_i2s_conf) - msp_write_dem(client, 0x40, state->i2s_mode); - - /* step-by-step initialisation, as described in the manual */ - modus = msp34xxg_modus(client); - modus |= tuner ? 0x100 : 0; - msp_write_dem(client, 0x30, modus); - - /* write the dsps that may have an influence on - standard/audio autodetection right now */ - msp34xxg_set_sources(client); - - msp_write_dsp(client, 0x0d, 0x1900); /* scart */ - msp_write_dsp(client, 0x0e, 0x3000); /* FM */ - if (state->has_nicam) - msp_write_dsp(client, 0x10, 0x5a00); /* nicam */ - - /* set identification threshold. Personally, I - * I set it to a higher value than the default - * of 0x190 to ignore noisy stereo signals. - * this needs tuning. (recommended range 0x00a0-0x03c0) - * 0x7f0 = forced mono mode - * - * a2 threshold for stereo/bilingual. - * Note: this register is part of the Manual/Compatibility mode. - * It is supported by all 'G'-family chips. - */ - msp_write_dem(client, 0x22, msp_stereo_thresh); -} - -int msp34xxg_thread(void *data) -{ - struct i2c_client *client = data; - struct msp_state *state = to_state(i2c_get_clientdata(client)); - int val, i; - - v4l_dbg(1, msp_debug, client, "msp34xxg daemon started\n"); - state->detected_std = V4L2_STD_ALL; - set_freezable(); - for (;;) { - v4l_dbg(2, msp_debug, client, "msp34xxg thread: sleep\n"); - msp_sleep(state, -1); - v4l_dbg(2, msp_debug, client, "msp34xxg thread: wakeup\n"); - -restart: - v4l_dbg(1, msp_debug, client, "thread: restart scan\n"); - state->restart = 0; - if (kthread_should_stop()) - break; - - if (state->mode == MSP_MODE_EXTERN) { - /* no carrier scan needed, just unmute */ - v4l_dbg(1, msp_debug, client, - "thread: no carrier scan\n"); - state->scan_in_progress = 0; - msp_update_volume(state); - continue; - } - - /* setup the chip*/ - msp34xxg_reset(client); - state->std = state->radio ? 0x40 : - (state->force_btsc && msp_standard == 1) ? 32 : msp_standard; - msp_write_dem(client, 0x20, state->std); - /* start autodetect */ - if (state->std != 1) - goto unmute; - - /* watch autodetect */ - v4l_dbg(1, msp_debug, client, - "started autodetect, waiting for result\n"); - for (i = 0; i < 10; i++) { - if (msp_sleep(state, 100)) - goto restart; - - /* check results */ - val = msp_read_dem(client, 0x7e); - if (val < 0x07ff) { - state->std = val; - break; - } - v4l_dbg(2, msp_debug, client, - "detection still in progress\n"); - } - if (state->std == 1) { - v4l_dbg(1, msp_debug, client, - "detection still in progress after 10 tries. giving up.\n"); - continue; - } - -unmute: - v4l_dbg(1, msp_debug, client, - "detected standard: %s (0x%04x)\n", - msp_standard_std_name(state->std), state->std); - state->detected_std = msp_standard_std(state->std); - - if (state->std == 9) { - /* AM NICAM mode */ - msp_write_dsp(client, 0x0e, 0x7c00); - } - - /* unmute: dispatch sound to scart output, set scart volume */ - msp_update_volume(state); - - /* restore ACB */ - if (msp_write_dsp(client, 0x13, state->acb)) - return -1; - - /* the periodic stereo/SAP check is only relevant for - the 0x20 standard (BTSC) */ - if (state->std != 0x20) - continue; - - state->watch_stereo = 1; - - /* monitor tv audio mode, the first time don't wait - in order to get a quick stereo/SAP update */ - watch_stereo(client); - while (state->watch_stereo) { - watch_stereo(client); - if (msp_sleep(state, 5000)) - goto restart; - } - } - v4l_dbg(1, msp_debug, client, "thread: exit\n"); - return 0; -} - -static int msp34xxg_detect_stereo(struct i2c_client *client) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - int status = msp_read_dem(client, 0x0200); - int is_bilingual = status & 0x100; - int is_stereo = status & 0x40; - int oldrx = state->rxsubchans; - - if (state->mode == MSP_MODE_EXTERN) - return 0; - - state->rxsubchans = 0; - if (is_stereo) - state->rxsubchans = V4L2_TUNER_SUB_STEREO; - else - state->rxsubchans = V4L2_TUNER_SUB_MONO; - if (is_bilingual) { - if (state->std == 0x20) - state->rxsubchans |= V4L2_TUNER_SUB_SAP; - else - state->rxsubchans = - V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - } - v4l_dbg(1, msp_debug, client, - "status=0x%x, stereo=%d, bilingual=%d -> rxsubchans=%d\n", - status, is_stereo, is_bilingual, state->rxsubchans); - return (oldrx != state->rxsubchans); -} - -static void msp34xxg_set_audmode(struct i2c_client *client) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - - if (state->std == 0x20) { - if ((state->rxsubchans & V4L2_TUNER_SUB_SAP) && - (state->audmode == V4L2_TUNER_MODE_LANG1_LANG2 || - state->audmode == V4L2_TUNER_MODE_LANG2)) { - msp_write_dem(client, 0x20, 0x21); - } else { - msp_write_dem(client, 0x20, 0x20); - } - } - - msp34xxg_set_sources(client); -} - -void msp_set_audmode(struct i2c_client *client) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - - switch (state->opmode) { - case OPMODE_MANUAL: - case OPMODE_AUTODETECT: - msp3400c_set_audmode(client); - break; - case OPMODE_AUTOSELECT: - msp34xxg_set_audmode(client); - break; - } -} - -int msp_detect_stereo(struct i2c_client *client) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - - switch (state->opmode) { - case OPMODE_MANUAL: - case OPMODE_AUTODETECT: - return msp3400c_detect_stereo(client); - case OPMODE_AUTOSELECT: - return msp34xxg_detect_stereo(client); - } - return 0; -} - diff --git a/drivers/media/video/mt9m032.c b/drivers/media/video/mt9m032.c deleted file mode 100644 index 445359c9611..00000000000 --- a/drivers/media/video/mt9m032.c +++ /dev/null @@ -1,878 +0,0 @@ -/* - * Driver for MT9M032 CMOS Image Sensor from Micron - * - * Copyright (C) 2010-2011 Lund Engineering - * Contact: Gil Lund - * Author: Martin Hostettler - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "aptina-pll.h" - -/* - * width and height include active boundary and black parts - * - * column 0- 15 active boundary - * column 16-1455 image - * column 1456-1471 active boundary - * column 1472-1599 black - * - * row 0- 51 black - * row 53- 59 active boundary - * row 60-1139 image - * row 1140-1147 active boundary - * row 1148-1151 black - */ - -#define MT9M032_PIXEL_ARRAY_WIDTH 1600 -#define MT9M032_PIXEL_ARRAY_HEIGHT 1152 - -#define MT9M032_CHIP_VERSION 0x00 -#define MT9M032_CHIP_VERSION_VALUE 0x1402 -#define MT9M032_ROW_START 0x01 -#define MT9M032_ROW_START_MIN 0 -#define MT9M032_ROW_START_MAX 1152 -#define MT9M032_ROW_START_DEF 60 -#define MT9M032_COLUMN_START 0x02 -#define MT9M032_COLUMN_START_MIN 0 -#define MT9M032_COLUMN_START_MAX 1600 -#define MT9M032_COLUMN_START_DEF 16 -#define MT9M032_ROW_SIZE 0x03 -#define MT9M032_ROW_SIZE_MIN 32 -#define MT9M032_ROW_SIZE_MAX 1152 -#define MT9M032_ROW_SIZE_DEF 1080 -#define MT9M032_COLUMN_SIZE 0x04 -#define MT9M032_COLUMN_SIZE_MIN 32 -#define MT9M032_COLUMN_SIZE_MAX 1600 -#define MT9M032_COLUMN_SIZE_DEF 1440 -#define MT9M032_HBLANK 0x05 -#define MT9M032_VBLANK 0x06 -#define MT9M032_VBLANK_MAX 0x7ff -#define MT9M032_SHUTTER_WIDTH_HIGH 0x08 -#define MT9M032_SHUTTER_WIDTH_LOW 0x09 -#define MT9M032_SHUTTER_WIDTH_MIN 1 -#define MT9M032_SHUTTER_WIDTH_MAX 1048575 -#define MT9M032_SHUTTER_WIDTH_DEF 1943 -#define MT9M032_PIX_CLK_CTRL 0x0a -#define MT9M032_PIX_CLK_CTRL_INV_PIXCLK 0x8000 -#define MT9M032_RESTART 0x0b -#define MT9M032_RESET 0x0d -#define MT9M032_PLL_CONFIG1 0x11 -#define MT9M032_PLL_CONFIG1_OUTDIV_MASK 0x3f -#define MT9M032_PLL_CONFIG1_MUL_SHIFT 8 -#define MT9M032_READ_MODE1 0x1e -#define MT9M032_READ_MODE2 0x20 -#define MT9M032_READ_MODE2_VFLIP_SHIFT 15 -#define MT9M032_READ_MODE2_HFLIP_SHIFT 14 -#define MT9M032_READ_MODE2_ROW_BLC 0x40 -#define MT9M032_GAIN_GREEN1 0x2b -#define MT9M032_GAIN_BLUE 0x2c -#define MT9M032_GAIN_RED 0x2d -#define MT9M032_GAIN_GREEN2 0x2e - -/* write only */ -#define MT9M032_GAIN_ALL 0x35 -#define MT9M032_GAIN_DIGITAL_MASK 0x7f -#define MT9M032_GAIN_DIGITAL_SHIFT 8 -#define MT9M032_GAIN_AMUL_SHIFT 6 -#define MT9M032_GAIN_ANALOG_MASK 0x3f -#define MT9M032_FORMATTER1 0x9e -#define MT9M032_FORMATTER2 0x9f -#define MT9M032_FORMATTER2_DOUT_EN 0x1000 -#define MT9M032_FORMATTER2_PIXCLK_EN 0x2000 - -/* - * The available MT9M032 datasheet is missing documentation for register 0x10 - * MT9P031 seems to be close enough, so use constants from that datasheet for - * now. - * But keep the name MT9P031 to remind us, that this isn't really confirmed - * for this sensor. - */ -#define MT9P031_PLL_CONTROL 0x10 -#define MT9P031_PLL_CONTROL_PWROFF 0x0050 -#define MT9P031_PLL_CONTROL_PWRON 0x0051 -#define MT9P031_PLL_CONTROL_USEPLL 0x0052 -#define MT9P031_PLL_CONFIG2 0x11 -#define MT9P031_PLL_CONFIG2_P1_DIV_MASK 0x1f - -struct mt9m032 { - struct v4l2_subdev subdev; - struct media_pad pad; - struct mt9m032_platform_data *pdata; - - unsigned int pix_clock; - - struct v4l2_ctrl_handler ctrls; - struct { - struct v4l2_ctrl *hflip; - struct v4l2_ctrl *vflip; - }; - - struct mutex lock; /* Protects streaming, format, interval and crop */ - - bool streaming; - - struct v4l2_mbus_framefmt format; - struct v4l2_rect crop; - struct v4l2_fract frame_interval; -}; - -#define to_mt9m032(sd) container_of(sd, struct mt9m032, subdev) -#define to_dev(sensor) \ - (&((struct i2c_client *)v4l2_get_subdevdata(&(sensor)->subdev))->dev) - -static int mt9m032_read(struct i2c_client *client, u8 reg) -{ - return i2c_smbus_read_word_swapped(client, reg); -} - -static int mt9m032_write(struct i2c_client *client, u8 reg, const u16 data) -{ - return i2c_smbus_write_word_swapped(client, reg, data); -} - -static u32 mt9m032_row_time(struct mt9m032 *sensor, unsigned int width) -{ - unsigned int effective_width; - u32 ns; - - effective_width = width + 716; /* empirical value */ - ns = div_u64(1000000000ULL * effective_width, sensor->pix_clock); - dev_dbg(to_dev(sensor), "MT9M032 line time: %u ns\n", ns); - return ns; -} - -static int mt9m032_update_timing(struct mt9m032 *sensor, - struct v4l2_fract *interval) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); - struct v4l2_rect *crop = &sensor->crop; - unsigned int min_vblank; - unsigned int vblank; - u32 row_time; - - if (!interval) - interval = &sensor->frame_interval; - - row_time = mt9m032_row_time(sensor, crop->width); - - vblank = div_u64(1000000000ULL * interval->numerator, - (u64)row_time * interval->denominator) - - crop->height; - - if (vblank > MT9M032_VBLANK_MAX) { - /* hardware limits to 11 bit values */ - interval->denominator = 1000; - interval->numerator = - div_u64((crop->height + MT9M032_VBLANK_MAX) * - (u64)row_time * interval->denominator, - 1000000000ULL); - vblank = div_u64(1000000000ULL * interval->numerator, - (u64)row_time * interval->denominator) - - crop->height; - } - /* enforce minimal 1.6ms blanking time. */ - min_vblank = 1600000 / row_time; - vblank = clamp_t(unsigned int, vblank, min_vblank, MT9M032_VBLANK_MAX); - - return mt9m032_write(client, MT9M032_VBLANK, vblank); -} - -static int mt9m032_update_geom_timing(struct mt9m032 *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); - int ret; - - ret = mt9m032_write(client, MT9M032_COLUMN_SIZE, - sensor->crop.width - 1); - if (!ret) - ret = mt9m032_write(client, MT9M032_ROW_SIZE, - sensor->crop.height - 1); - if (!ret) - ret = mt9m032_write(client, MT9M032_COLUMN_START, - sensor->crop.left); - if (!ret) - ret = mt9m032_write(client, MT9M032_ROW_START, - sensor->crop.top); - if (!ret) - ret = mt9m032_update_timing(sensor, NULL); - return ret; -} - -static int update_formatter2(struct mt9m032 *sensor, bool streaming) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); - u16 reg_val = MT9M032_FORMATTER2_DOUT_EN - | 0x0070; /* parts reserved! */ - /* possibly for changing to 14-bit mode */ - - if (streaming) - reg_val |= MT9M032_FORMATTER2_PIXCLK_EN; /* pixclock enable */ - - return mt9m032_write(client, MT9M032_FORMATTER2, reg_val); -} - -static int mt9m032_setup_pll(struct mt9m032 *sensor) -{ - static const struct aptina_pll_limits limits = { - .ext_clock_min = 8000000, - .ext_clock_max = 16500000, - .int_clock_min = 2000000, - .int_clock_max = 24000000, - .out_clock_min = 322000000, - .out_clock_max = 693000000, - .pix_clock_max = 99000000, - .n_min = 1, - .n_max = 64, - .m_min = 16, - .m_max = 255, - .p1_min = 1, - .p1_max = 128, - }; - - struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); - struct mt9m032_platform_data *pdata = sensor->pdata; - struct aptina_pll pll; - int ret; - - pll.ext_clock = pdata->ext_clock; - pll.pix_clock = pdata->pix_clock; - - ret = aptina_pll_calculate(&client->dev, &limits, &pll); - if (ret < 0) - return ret; - - sensor->pix_clock = pdata->pix_clock; - - ret = mt9m032_write(client, MT9M032_PLL_CONFIG1, - (pll.m << MT9M032_PLL_CONFIG1_MUL_SHIFT) - | (pll.p1 - 1)); - if (!ret) - ret = mt9m032_write(client, MT9P031_PLL_CONFIG2, pll.n - 1); - if (!ret) - ret = mt9m032_write(client, MT9P031_PLL_CONTROL, - MT9P031_PLL_CONTROL_PWRON | - MT9P031_PLL_CONTROL_USEPLL); - if (!ret) /* more reserved, Continuous, Master Mode */ - ret = mt9m032_write(client, MT9M032_READ_MODE1, 0x8006); - if (!ret) /* Set 14-bit mode, select 7 divider */ - ret = mt9m032_write(client, MT9M032_FORMATTER1, 0x111e); - - return ret; -} - -/* ----------------------------------------------------------------------------- - * Subdev pad operations - */ - -static int mt9m032_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->index != 0) - return -EINVAL; - - code->code = V4L2_MBUS_FMT_Y8_1X8; - return 0; -} - -static int mt9m032_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_frame_size_enum *fse) -{ - if (fse->index != 0 || fse->code != V4L2_MBUS_FMT_Y8_1X8) - return -EINVAL; - - fse->min_width = MT9M032_COLUMN_SIZE_DEF; - fse->max_width = MT9M032_COLUMN_SIZE_DEF; - fse->min_height = MT9M032_ROW_SIZE_DEF; - fse->max_height = MT9M032_ROW_SIZE_DEF; - - return 0; -} - -/** - * __mt9m032_get_pad_crop() - get crop rect - * @sensor: pointer to the sensor struct - * @fh: file handle for getting the try crop rect from - * @which: select try or active crop rect - * - * Returns a pointer the current active or fh relative try crop rect - */ -static struct v4l2_rect * -__mt9m032_get_pad_crop(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh, - enum v4l2_subdev_format_whence which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(fh, 0); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &sensor->crop; - default: - return NULL; - } -} - -/** - * __mt9m032_get_pad_format() - get format - * @sensor: pointer to the sensor struct - * @fh: file handle for getting the try format from - * @which: select try or active format - * - * Returns a pointer the current active or fh relative try format - */ -static struct v4l2_mbus_framefmt * -__mt9m032_get_pad_format(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh, - enum v4l2_subdev_format_whence which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(fh, 0); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &sensor->format; - default: - return NULL; - } -} - -static int mt9m032_get_pad_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *fmt) -{ - struct mt9m032 *sensor = to_mt9m032(subdev); - - mutex_lock(&sensor->lock); - fmt->format = *__mt9m032_get_pad_format(sensor, fh, fmt->which); - mutex_unlock(&sensor->lock); - - return 0; -} - -static int mt9m032_set_pad_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *fmt) -{ - struct mt9m032 *sensor = to_mt9m032(subdev); - int ret; - - mutex_lock(&sensor->lock); - - if (sensor->streaming && fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - ret = -EBUSY; - goto done; - } - - /* Scaling is not supported, the format is thus fixed. */ - fmt->format = *__mt9m032_get_pad_format(sensor, fh, fmt->which); - ret = 0; - -done: - mutex_unlock(&sensor->lock); - return ret; -} - -static int mt9m032_get_pad_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) -{ - struct mt9m032 *sensor = to_mt9m032(subdev); - - mutex_lock(&sensor->lock); - crop->rect = *__mt9m032_get_pad_crop(sensor, fh, crop->which); - mutex_unlock(&sensor->lock); - - return 0; -} - -static int mt9m032_set_pad_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) -{ - struct mt9m032 *sensor = to_mt9m032(subdev); - struct v4l2_mbus_framefmt *format; - struct v4l2_rect *__crop; - struct v4l2_rect rect; - int ret = 0; - - mutex_lock(&sensor->lock); - - if (sensor->streaming && crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - ret = -EBUSY; - goto done; - } - - /* Clamp the crop rectangle boundaries and align them to a multiple of 2 - * pixels to ensure a GRBG Bayer pattern. - */ - rect.left = clamp(ALIGN(crop->rect.left, 2), MT9M032_COLUMN_START_MIN, - MT9M032_COLUMN_START_MAX); - rect.top = clamp(ALIGN(crop->rect.top, 2), MT9M032_ROW_START_MIN, - MT9M032_ROW_START_MAX); - rect.width = clamp(ALIGN(crop->rect.width, 2), MT9M032_COLUMN_SIZE_MIN, - MT9M032_COLUMN_SIZE_MAX); - rect.height = clamp(ALIGN(crop->rect.height, 2), MT9M032_ROW_SIZE_MIN, - MT9M032_ROW_SIZE_MAX); - - rect.width = min(rect.width, MT9M032_PIXEL_ARRAY_WIDTH - rect.left); - rect.height = min(rect.height, MT9M032_PIXEL_ARRAY_HEIGHT - rect.top); - - __crop = __mt9m032_get_pad_crop(sensor, fh, crop->which); - - if (rect.width != __crop->width || rect.height != __crop->height) { - /* Reset the output image size if the crop rectangle size has - * been modified. - */ - format = __mt9m032_get_pad_format(sensor, fh, crop->which); - format->width = rect.width; - format->height = rect.height; - } - - *__crop = rect; - crop->rect = rect; - - if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) - ret = mt9m032_update_geom_timing(sensor); - -done: - mutex_unlock(&sensor->lock); - return ret; -} - -static int mt9m032_get_frame_interval(struct v4l2_subdev *subdev, - struct v4l2_subdev_frame_interval *fi) -{ - struct mt9m032 *sensor = to_mt9m032(subdev); - - mutex_lock(&sensor->lock); - memset(fi, 0, sizeof(*fi)); - fi->interval = sensor->frame_interval; - mutex_unlock(&sensor->lock); - - return 0; -} - -static int mt9m032_set_frame_interval(struct v4l2_subdev *subdev, - struct v4l2_subdev_frame_interval *fi) -{ - struct mt9m032 *sensor = to_mt9m032(subdev); - int ret; - - mutex_lock(&sensor->lock); - - if (sensor->streaming) { - ret = -EBUSY; - goto done; - } - - /* Avoid divisions by 0. */ - if (fi->interval.denominator == 0) - fi->interval.denominator = 1; - - ret = mt9m032_update_timing(sensor, &fi->interval); - if (!ret) - sensor->frame_interval = fi->interval; - -done: - mutex_unlock(&sensor->lock); - return ret; -} - -static int mt9m032_s_stream(struct v4l2_subdev *subdev, int streaming) -{ - struct mt9m032 *sensor = to_mt9m032(subdev); - int ret; - - mutex_lock(&sensor->lock); - ret = update_formatter2(sensor, streaming); - if (!ret) - sensor->streaming = streaming; - mutex_unlock(&sensor->lock); - - return ret; -} - -/* ----------------------------------------------------------------------------- - * V4L2 subdev core operations - */ - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int mt9m032_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct mt9m032 *sensor = to_mt9m032(sd); - struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); - int val; - - if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) - return -EINVAL; - if (reg->match.addr != client->addr) - return -ENODEV; - - val = mt9m032_read(client, reg->reg); - if (val < 0) - return -EIO; - - reg->size = 2; - reg->val = val; - - return 0; -} - -static int mt9m032_s_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct mt9m032 *sensor = to_mt9m032(sd); - struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); - - if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) - return -EINVAL; - - if (reg->match.addr != client->addr) - return -ENODEV; - - return mt9m032_write(client, reg->reg, reg->val); -} -#endif - -/* ----------------------------------------------------------------------------- - * V4L2 subdev control operations - */ - -static int update_read_mode2(struct mt9m032 *sensor, bool vflip, bool hflip) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); - int reg_val = (vflip << MT9M032_READ_MODE2_VFLIP_SHIFT) - | (hflip << MT9M032_READ_MODE2_HFLIP_SHIFT) - | MT9M032_READ_MODE2_ROW_BLC - | 0x0007; - - return mt9m032_write(client, MT9M032_READ_MODE2, reg_val); -} - -static int mt9m032_set_gain(struct mt9m032 *sensor, s32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); - int digital_gain_val; /* in 1/8th (0..127) */ - int analog_mul; /* 0 or 1 */ - int analog_gain_val; /* in 1/16th. (0..63) */ - u16 reg_val; - - digital_gain_val = 51; /* from setup example */ - - if (val < 63) { - analog_mul = 0; - analog_gain_val = val; - } else { - analog_mul = 1; - analog_gain_val = val / 2; - } - - /* a_gain = (1 + analog_mul) + (analog_gain_val + 1) / 16 */ - /* overall_gain = a_gain * (1 + digital_gain_val / 8) */ - - reg_val = ((digital_gain_val & MT9M032_GAIN_DIGITAL_MASK) - << MT9M032_GAIN_DIGITAL_SHIFT) - | ((analog_mul & 1) << MT9M032_GAIN_AMUL_SHIFT) - | (analog_gain_val & MT9M032_GAIN_ANALOG_MASK); - - return mt9m032_write(client, MT9M032_GAIN_ALL, reg_val); -} - -static int mt9m032_try_ctrl(struct v4l2_ctrl *ctrl) -{ - if (ctrl->id == V4L2_CID_GAIN && ctrl->val >= 63) { - /* round because of multiplier used for values >= 63 */ - ctrl->val &= ~1; - } - - return 0; -} - -static int mt9m032_set_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9m032 *sensor = - container_of(ctrl->handler, struct mt9m032, ctrls); - struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); - int ret; - - switch (ctrl->id) { - case V4L2_CID_GAIN: - return mt9m032_set_gain(sensor, ctrl->val); - - case V4L2_CID_HFLIP: - /* case V4L2_CID_VFLIP: -- In the same cluster */ - return update_read_mode2(sensor, sensor->vflip->val, - sensor->hflip->val); - - case V4L2_CID_EXPOSURE: - ret = mt9m032_write(client, MT9M032_SHUTTER_WIDTH_HIGH, - (ctrl->val >> 16) & 0xffff); - if (ret < 0) - return ret; - - return mt9m032_write(client, MT9M032_SHUTTER_WIDTH_LOW, - ctrl->val & 0xffff); - } - - return 0; -} - -static struct v4l2_ctrl_ops mt9m032_ctrl_ops = { - .s_ctrl = mt9m032_set_ctrl, - .try_ctrl = mt9m032_try_ctrl, -}; - -/* -------------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops mt9m032_core_ops = { -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = mt9m032_g_register, - .s_register = mt9m032_s_register, -#endif -}; - -static const struct v4l2_subdev_video_ops mt9m032_video_ops = { - .s_stream = mt9m032_s_stream, - .g_frame_interval = mt9m032_get_frame_interval, - .s_frame_interval = mt9m032_set_frame_interval, -}; - -static const struct v4l2_subdev_pad_ops mt9m032_pad_ops = { - .enum_mbus_code = mt9m032_enum_mbus_code, - .enum_frame_size = mt9m032_enum_frame_size, - .get_fmt = mt9m032_get_pad_format, - .set_fmt = mt9m032_set_pad_format, - .set_crop = mt9m032_set_pad_crop, - .get_crop = mt9m032_get_pad_crop, -}; - -static const struct v4l2_subdev_ops mt9m032_ops = { - .core = &mt9m032_core_ops, - .video = &mt9m032_video_ops, - .pad = &mt9m032_pad_ops, -}; - -/* ----------------------------------------------------------------------------- - * Driver initialization and probing - */ - -static int mt9m032_probe(struct i2c_client *client, - const struct i2c_device_id *devid) -{ - struct mt9m032_platform_data *pdata = client->dev.platform_data; - struct i2c_adapter *adapter = client->adapter; - struct mt9m032 *sensor; - int chip_version; - int ret; - - if (pdata == NULL) { - dev_err(&client->dev, "No platform data\n"); - return -EINVAL; - } - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { - dev_warn(&client->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); - return -EIO; - } - - if (!client->dev.platform_data) - return -ENODEV; - - sensor = kzalloc(sizeof(*sensor), GFP_KERNEL); - if (sensor == NULL) - return -ENOMEM; - - mutex_init(&sensor->lock); - - sensor->pdata = pdata; - - v4l2_i2c_subdev_init(&sensor->subdev, client, &mt9m032_ops); - sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - chip_version = mt9m032_read(client, MT9M032_CHIP_VERSION); - if (chip_version != MT9M032_CHIP_VERSION_VALUE) { - dev_err(&client->dev, "MT9M032 not detected, wrong version " - "0x%04x\n", chip_version); - ret = -ENODEV; - goto error_sensor; - } - - dev_info(&client->dev, "MT9M032 detected at address 0x%02x\n", - client->addr); - - sensor->frame_interval.numerator = 1; - sensor->frame_interval.denominator = 30; - - sensor->crop.left = MT9M032_COLUMN_START_DEF; - sensor->crop.top = MT9M032_ROW_START_DEF; - sensor->crop.width = MT9M032_COLUMN_SIZE_DEF; - sensor->crop.height = MT9M032_ROW_SIZE_DEF; - - sensor->format.width = sensor->crop.width; - sensor->format.height = sensor->crop.height; - sensor->format.code = V4L2_MBUS_FMT_Y8_1X8; - sensor->format.field = V4L2_FIELD_NONE; - sensor->format.colorspace = V4L2_COLORSPACE_SRGB; - - v4l2_ctrl_handler_init(&sensor->ctrls, 5); - - v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops, - V4L2_CID_GAIN, 0, 127, 1, 64); - - sensor->hflip = v4l2_ctrl_new_std(&sensor->ctrls, - &mt9m032_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - sensor->vflip = v4l2_ctrl_new_std(&sensor->ctrls, - &mt9m032_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - - v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops, - V4L2_CID_EXPOSURE, MT9M032_SHUTTER_WIDTH_MIN, - MT9M032_SHUTTER_WIDTH_MAX, 1, - MT9M032_SHUTTER_WIDTH_DEF); - v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops, - V4L2_CID_PIXEL_RATE, pdata->pix_clock, - pdata->pix_clock, 1, pdata->pix_clock); - - if (sensor->ctrls.error) { - ret = sensor->ctrls.error; - dev_err(&client->dev, "control initialization error %d\n", ret); - goto error_ctrl; - } - - v4l2_ctrl_cluster(2, &sensor->hflip); - - sensor->subdev.ctrl_handler = &sensor->ctrls; - sensor->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&sensor->subdev.entity, 1, &sensor->pad, 0); - if (ret < 0) - goto error_ctrl; - - ret = mt9m032_write(client, MT9M032_RESET, 1); /* reset on */ - if (ret < 0) - goto error_entity; - mt9m032_write(client, MT9M032_RESET, 0); /* reset off */ - if (ret < 0) - goto error_entity; - - ret = mt9m032_setup_pll(sensor); - if (ret < 0) - goto error_entity; - usleep_range(10000, 11000); - - ret = v4l2_ctrl_handler_setup(&sensor->ctrls); - if (ret < 0) - goto error_entity; - - /* SIZE */ - ret = mt9m032_update_geom_timing(sensor); - if (ret < 0) - goto error_entity; - - ret = mt9m032_write(client, 0x41, 0x0000); /* reserved !!! */ - if (ret < 0) - goto error_entity; - ret = mt9m032_write(client, 0x42, 0x0003); /* reserved !!! */ - if (ret < 0) - goto error_entity; - ret = mt9m032_write(client, 0x43, 0x0003); /* reserved !!! */ - if (ret < 0) - goto error_entity; - ret = mt9m032_write(client, 0x7f, 0x0000); /* reserved !!! */ - if (ret < 0) - goto error_entity; - if (sensor->pdata->invert_pixclock) { - ret = mt9m032_write(client, MT9M032_PIX_CLK_CTRL, - MT9M032_PIX_CLK_CTRL_INV_PIXCLK); - if (ret < 0) - goto error_entity; - } - - ret = mt9m032_write(client, MT9M032_RESTART, 1); /* Restart on */ - if (ret < 0) - goto error_entity; - msleep(100); - ret = mt9m032_write(client, MT9M032_RESTART, 0); /* Restart off */ - if (ret < 0) - goto error_entity; - msleep(100); - ret = update_formatter2(sensor, false); - if (ret < 0) - goto error_entity; - - return ret; - -error_entity: - media_entity_cleanup(&sensor->subdev.entity); -error_ctrl: - v4l2_ctrl_handler_free(&sensor->ctrls); -error_sensor: - mutex_destroy(&sensor->lock); - kfree(sensor); - return ret; -} - -static int mt9m032_remove(struct i2c_client *client) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct mt9m032 *sensor = to_mt9m032(subdev); - - v4l2_device_unregister_subdev(subdev); - v4l2_ctrl_handler_free(&sensor->ctrls); - media_entity_cleanup(&subdev->entity); - mutex_destroy(&sensor->lock); - kfree(sensor); - return 0; -} - -static const struct i2c_device_id mt9m032_id_table[] = { - { MT9M032_NAME, 0 }, - { } -}; - -MODULE_DEVICE_TABLE(i2c, mt9m032_id_table); - -static struct i2c_driver mt9m032_i2c_driver = { - .driver = { - .name = MT9M032_NAME, - }, - .probe = mt9m032_probe, - .remove = mt9m032_remove, - .id_table = mt9m032_id_table, -}; - -module_i2c_driver(mt9m032_i2c_driver); - -MODULE_AUTHOR("Martin Hostettler "); -MODULE_DESCRIPTION("MT9M032 camera sensor driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/mt9p031.c b/drivers/media/video/mt9p031.c deleted file mode 100644 index 3be537ef22d..00000000000 --- a/drivers/media/video/mt9p031.c +++ /dev/null @@ -1,1071 +0,0 @@ -/* - * Driver for MT9P031 CMOS Image Sensor from Aptina - * - * Copyright (C) 2011, Laurent Pinchart - * Copyright (C) 2011, Javier Martin - * Copyright (C) 2011, Guennadi Liakhovetski - * - * Based on the MT9V032 driver and Bastian Hecht's code. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "aptina-pll.h" - -#define MT9P031_PIXEL_ARRAY_WIDTH 2752 -#define MT9P031_PIXEL_ARRAY_HEIGHT 2004 - -#define MT9P031_CHIP_VERSION 0x00 -#define MT9P031_CHIP_VERSION_VALUE 0x1801 -#define MT9P031_ROW_START 0x01 -#define MT9P031_ROW_START_MIN 0 -#define MT9P031_ROW_START_MAX 2004 -#define MT9P031_ROW_START_DEF 54 -#define MT9P031_COLUMN_START 0x02 -#define MT9P031_COLUMN_START_MIN 0 -#define MT9P031_COLUMN_START_MAX 2750 -#define MT9P031_COLUMN_START_DEF 16 -#define MT9P031_WINDOW_HEIGHT 0x03 -#define MT9P031_WINDOW_HEIGHT_MIN 2 -#define MT9P031_WINDOW_HEIGHT_MAX 2006 -#define MT9P031_WINDOW_HEIGHT_DEF 1944 -#define MT9P031_WINDOW_WIDTH 0x04 -#define MT9P031_WINDOW_WIDTH_MIN 2 -#define MT9P031_WINDOW_WIDTH_MAX 2752 -#define MT9P031_WINDOW_WIDTH_DEF 2592 -#define MT9P031_HORIZONTAL_BLANK 0x05 -#define MT9P031_HORIZONTAL_BLANK_MIN 0 -#define MT9P031_HORIZONTAL_BLANK_MAX 4095 -#define MT9P031_VERTICAL_BLANK 0x06 -#define MT9P031_VERTICAL_BLANK_MIN 0 -#define MT9P031_VERTICAL_BLANK_MAX 4095 -#define MT9P031_VERTICAL_BLANK_DEF 25 -#define MT9P031_OUTPUT_CONTROL 0x07 -#define MT9P031_OUTPUT_CONTROL_CEN 2 -#define MT9P031_OUTPUT_CONTROL_SYN 1 -#define MT9P031_OUTPUT_CONTROL_DEF 0x1f82 -#define MT9P031_SHUTTER_WIDTH_UPPER 0x08 -#define MT9P031_SHUTTER_WIDTH_LOWER 0x09 -#define MT9P031_SHUTTER_WIDTH_MIN 1 -#define MT9P031_SHUTTER_WIDTH_MAX 1048575 -#define MT9P031_SHUTTER_WIDTH_DEF 1943 -#define MT9P031_PLL_CONTROL 0x10 -#define MT9P031_PLL_CONTROL_PWROFF 0x0050 -#define MT9P031_PLL_CONTROL_PWRON 0x0051 -#define MT9P031_PLL_CONTROL_USEPLL 0x0052 -#define MT9P031_PLL_CONFIG_1 0x11 -#define MT9P031_PLL_CONFIG_2 0x12 -#define MT9P031_PIXEL_CLOCK_CONTROL 0x0a -#define MT9P031_FRAME_RESTART 0x0b -#define MT9P031_SHUTTER_DELAY 0x0c -#define MT9P031_RST 0x0d -#define MT9P031_RST_ENABLE 1 -#define MT9P031_RST_DISABLE 0 -#define MT9P031_READ_MODE_1 0x1e -#define MT9P031_READ_MODE_2 0x20 -#define MT9P031_READ_MODE_2_ROW_MIR (1 << 15) -#define MT9P031_READ_MODE_2_COL_MIR (1 << 14) -#define MT9P031_READ_MODE_2_ROW_BLC (1 << 6) -#define MT9P031_ROW_ADDRESS_MODE 0x22 -#define MT9P031_COLUMN_ADDRESS_MODE 0x23 -#define MT9P031_GLOBAL_GAIN 0x35 -#define MT9P031_GLOBAL_GAIN_MIN 8 -#define MT9P031_GLOBAL_GAIN_MAX 1024 -#define MT9P031_GLOBAL_GAIN_DEF 8 -#define MT9P031_GLOBAL_GAIN_MULT (1 << 6) -#define MT9P031_ROW_BLACK_TARGET 0x49 -#define MT9P031_ROW_BLACK_DEF_OFFSET 0x4b -#define MT9P031_GREEN1_OFFSET 0x60 -#define MT9P031_GREEN2_OFFSET 0x61 -#define MT9P031_BLACK_LEVEL_CALIBRATION 0x62 -#define MT9P031_BLC_MANUAL_BLC (1 << 0) -#define MT9P031_RED_OFFSET 0x63 -#define MT9P031_BLUE_OFFSET 0x64 -#define MT9P031_TEST_PATTERN 0xa0 -#define MT9P031_TEST_PATTERN_SHIFT 3 -#define MT9P031_TEST_PATTERN_ENABLE (1 << 0) -#define MT9P031_TEST_PATTERN_DISABLE (0 << 0) -#define MT9P031_TEST_PATTERN_GREEN 0xa1 -#define MT9P031_TEST_PATTERN_RED 0xa2 -#define MT9P031_TEST_PATTERN_BLUE 0xa3 - -enum mt9p031_model { - MT9P031_MODEL_COLOR, - MT9P031_MODEL_MONOCHROME, -}; - -struct mt9p031 { - struct v4l2_subdev subdev; - struct media_pad pad; - struct v4l2_rect crop; /* Sensor window */ - struct v4l2_mbus_framefmt format; - struct mt9p031_platform_data *pdata; - struct mutex power_lock; /* lock to protect power_count */ - int power_count; - - enum mt9p031_model model; - struct aptina_pll pll; - int reset; - - struct v4l2_ctrl_handler ctrls; - struct v4l2_ctrl *blc_auto; - struct v4l2_ctrl *blc_offset; - - /* Registers cache */ - u16 output_control; - u16 mode2; -}; - -static struct mt9p031 *to_mt9p031(struct v4l2_subdev *sd) -{ - return container_of(sd, struct mt9p031, subdev); -} - -static int mt9p031_read(struct i2c_client *client, u8 reg) -{ - return i2c_smbus_read_word_swapped(client, reg); -} - -static int mt9p031_write(struct i2c_client *client, u8 reg, u16 data) -{ - return i2c_smbus_write_word_swapped(client, reg, data); -} - -static int mt9p031_set_output_control(struct mt9p031 *mt9p031, u16 clear, - u16 set) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); - u16 value = (mt9p031->output_control & ~clear) | set; - int ret; - - ret = mt9p031_write(client, MT9P031_OUTPUT_CONTROL, value); - if (ret < 0) - return ret; - - mt9p031->output_control = value; - return 0; -} - -static int mt9p031_set_mode2(struct mt9p031 *mt9p031, u16 clear, u16 set) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); - u16 value = (mt9p031->mode2 & ~clear) | set; - int ret; - - ret = mt9p031_write(client, MT9P031_READ_MODE_2, value); - if (ret < 0) - return ret; - - mt9p031->mode2 = value; - return 0; -} - -static int mt9p031_reset(struct mt9p031 *mt9p031) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); - int ret; - - /* Disable chip output, synchronous option update */ - ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_ENABLE); - if (ret < 0) - return ret; - ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_DISABLE); - if (ret < 0) - return ret; - - return mt9p031_set_output_control(mt9p031, MT9P031_OUTPUT_CONTROL_CEN, - 0); -} - -static int mt9p031_pll_setup(struct mt9p031 *mt9p031) -{ - static const struct aptina_pll_limits limits = { - .ext_clock_min = 6000000, - .ext_clock_max = 27000000, - .int_clock_min = 2000000, - .int_clock_max = 13500000, - .out_clock_min = 180000000, - .out_clock_max = 360000000, - .pix_clock_max = 96000000, - .n_min = 1, - .n_max = 64, - .m_min = 16, - .m_max = 255, - .p1_min = 1, - .p1_max = 128, - }; - - struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); - struct mt9p031_platform_data *pdata = mt9p031->pdata; - - mt9p031->pll.ext_clock = pdata->ext_freq; - mt9p031->pll.pix_clock = pdata->target_freq; - - return aptina_pll_calculate(&client->dev, &limits, &mt9p031->pll); -} - -static int mt9p031_pll_enable(struct mt9p031 *mt9p031) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); - int ret; - - ret = mt9p031_write(client, MT9P031_PLL_CONTROL, - MT9P031_PLL_CONTROL_PWRON); - if (ret < 0) - return ret; - - ret = mt9p031_write(client, MT9P031_PLL_CONFIG_1, - (mt9p031->pll.m << 8) | (mt9p031->pll.n - 1)); - if (ret < 0) - return ret; - - ret = mt9p031_write(client, MT9P031_PLL_CONFIG_2, mt9p031->pll.p1 - 1); - if (ret < 0) - return ret; - - usleep_range(1000, 2000); - ret = mt9p031_write(client, MT9P031_PLL_CONTROL, - MT9P031_PLL_CONTROL_PWRON | - MT9P031_PLL_CONTROL_USEPLL); - return ret; -} - -static inline int mt9p031_pll_disable(struct mt9p031 *mt9p031) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); - - return mt9p031_write(client, MT9P031_PLL_CONTROL, - MT9P031_PLL_CONTROL_PWROFF); -} - -static int mt9p031_power_on(struct mt9p031 *mt9p031) -{ - /* Ensure RESET_BAR is low */ - if (mt9p031->reset != -1) { - gpio_set_value(mt9p031->reset, 0); - usleep_range(1000, 2000); - } - - /* Emable clock */ - if (mt9p031->pdata->set_xclk) - mt9p031->pdata->set_xclk(&mt9p031->subdev, - mt9p031->pdata->ext_freq); - - /* Now RESET_BAR must be high */ - if (mt9p031->reset != -1) { - gpio_set_value(mt9p031->reset, 1); - usleep_range(1000, 2000); - } - - return 0; -} - -static void mt9p031_power_off(struct mt9p031 *mt9p031) -{ - if (mt9p031->reset != -1) { - gpio_set_value(mt9p031->reset, 0); - usleep_range(1000, 2000); - } - - if (mt9p031->pdata->set_xclk) - mt9p031->pdata->set_xclk(&mt9p031->subdev, 0); -} - -static int __mt9p031_set_power(struct mt9p031 *mt9p031, bool on) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); - int ret; - - if (!on) { - mt9p031_power_off(mt9p031); - return 0; - } - - ret = mt9p031_power_on(mt9p031); - if (ret < 0) - return ret; - - ret = mt9p031_reset(mt9p031); - if (ret < 0) { - dev_err(&client->dev, "Failed to reset the camera\n"); - return ret; - } - - return v4l2_ctrl_handler_setup(&mt9p031->ctrls); -} - -/* ----------------------------------------------------------------------------- - * V4L2 subdev video operations - */ - -static int mt9p031_set_params(struct mt9p031 *mt9p031) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); - struct v4l2_mbus_framefmt *format = &mt9p031->format; - const struct v4l2_rect *crop = &mt9p031->crop; - unsigned int hblank; - unsigned int vblank; - unsigned int xskip; - unsigned int yskip; - unsigned int xbin; - unsigned int ybin; - int ret; - - /* Windows position and size. - * - * TODO: Make sure the start coordinates and window size match the - * skipping, binning and mirroring (see description of registers 2 and 4 - * in table 13, and Binning section on page 41). - */ - ret = mt9p031_write(client, MT9P031_COLUMN_START, crop->left); - if (ret < 0) - return ret; - ret = mt9p031_write(client, MT9P031_ROW_START, crop->top); - if (ret < 0) - return ret; - ret = mt9p031_write(client, MT9P031_WINDOW_WIDTH, crop->width - 1); - if (ret < 0) - return ret; - ret = mt9p031_write(client, MT9P031_WINDOW_HEIGHT, crop->height - 1); - if (ret < 0) - return ret; - - /* Row and column binning and skipping. Use the maximum binning value - * compatible with the skipping settings. - */ - xskip = DIV_ROUND_CLOSEST(crop->width, format->width); - yskip = DIV_ROUND_CLOSEST(crop->height, format->height); - xbin = 1 << (ffs(xskip) - 1); - ybin = 1 << (ffs(yskip) - 1); - - ret = mt9p031_write(client, MT9P031_COLUMN_ADDRESS_MODE, - ((xbin - 1) << 4) | (xskip - 1)); - if (ret < 0) - return ret; - ret = mt9p031_write(client, MT9P031_ROW_ADDRESS_MODE, - ((ybin - 1) << 4) | (yskip - 1)); - if (ret < 0) - return ret; - - /* Blanking - use minimum value for horizontal blanking and default - * value for vertical blanking. - */ - hblank = 346 * ybin + 64 + (80 >> max_t(unsigned int, xbin, 3)); - vblank = MT9P031_VERTICAL_BLANK_DEF; - - ret = mt9p031_write(client, MT9P031_HORIZONTAL_BLANK, hblank); - if (ret < 0) - return ret; - ret = mt9p031_write(client, MT9P031_VERTICAL_BLANK, vblank); - if (ret < 0) - return ret; - - return ret; -} - -static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable) -{ - struct mt9p031 *mt9p031 = to_mt9p031(subdev); - int ret; - - if (!enable) { - /* Stop sensor readout */ - ret = mt9p031_set_output_control(mt9p031, - MT9P031_OUTPUT_CONTROL_CEN, 0); - if (ret < 0) - return ret; - - return mt9p031_pll_disable(mt9p031); - } - - ret = mt9p031_set_params(mt9p031); - if (ret < 0) - return ret; - - /* Switch to master "normal" mode */ - ret = mt9p031_set_output_control(mt9p031, 0, - MT9P031_OUTPUT_CONTROL_CEN); - if (ret < 0) - return ret; - - return mt9p031_pll_enable(mt9p031); -} - -static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_mbus_code_enum *code) -{ - struct mt9p031 *mt9p031 = to_mt9p031(subdev); - - if (code->pad || code->index) - return -EINVAL; - - code->code = mt9p031->format.code; - return 0; -} - -static int mt9p031_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_frame_size_enum *fse) -{ - struct mt9p031 *mt9p031 = to_mt9p031(subdev); - - if (fse->index >= 8 || fse->code != mt9p031->format.code) - return -EINVAL; - - fse->min_width = MT9P031_WINDOW_WIDTH_DEF - / min_t(unsigned int, 7, fse->index + 1); - fse->max_width = fse->min_width; - fse->min_height = MT9P031_WINDOW_HEIGHT_DEF / (fse->index + 1); - fse->max_height = fse->min_height; - - return 0; -} - -static struct v4l2_mbus_framefmt * -__mt9p031_get_pad_format(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh, - unsigned int pad, u32 which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(fh, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &mt9p031->format; - default: - return NULL; - } -} - -static struct v4l2_rect * -__mt9p031_get_pad_crop(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh, - unsigned int pad, u32 which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(fh, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &mt9p031->crop; - default: - return NULL; - } -} - -static int mt9p031_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *fmt) -{ - struct mt9p031 *mt9p031 = to_mt9p031(subdev); - - fmt->format = *__mt9p031_get_pad_format(mt9p031, fh, fmt->pad, - fmt->which); - return 0; -} - -static int mt9p031_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *format) -{ - struct mt9p031 *mt9p031 = to_mt9p031(subdev); - struct v4l2_mbus_framefmt *__format; - struct v4l2_rect *__crop; - unsigned int width; - unsigned int height; - unsigned int hratio; - unsigned int vratio; - - __crop = __mt9p031_get_pad_crop(mt9p031, fh, format->pad, - format->which); - - /* Clamp the width and height to avoid dividing by zero. */ - width = clamp_t(unsigned int, ALIGN(format->format.width, 2), - max(__crop->width / 7, MT9P031_WINDOW_WIDTH_MIN), - __crop->width); - height = clamp_t(unsigned int, ALIGN(format->format.height, 2), - max(__crop->height / 8, MT9P031_WINDOW_HEIGHT_MIN), - __crop->height); - - hratio = DIV_ROUND_CLOSEST(__crop->width, width); - vratio = DIV_ROUND_CLOSEST(__crop->height, height); - - __format = __mt9p031_get_pad_format(mt9p031, fh, format->pad, - format->which); - __format->width = __crop->width / hratio; - __format->height = __crop->height / vratio; - - format->format = *__format; - - return 0; -} - -static int mt9p031_get_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) -{ - struct mt9p031 *mt9p031 = to_mt9p031(subdev); - - crop->rect = *__mt9p031_get_pad_crop(mt9p031, fh, crop->pad, - crop->which); - return 0; -} - -static int mt9p031_set_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) -{ - struct mt9p031 *mt9p031 = to_mt9p031(subdev); - struct v4l2_mbus_framefmt *__format; - struct v4l2_rect *__crop; - struct v4l2_rect rect; - - /* Clamp the crop rectangle boundaries and align them to a multiple of 2 - * pixels to ensure a GRBG Bayer pattern. - */ - rect.left = clamp(ALIGN(crop->rect.left, 2), MT9P031_COLUMN_START_MIN, - MT9P031_COLUMN_START_MAX); - rect.top = clamp(ALIGN(crop->rect.top, 2), MT9P031_ROW_START_MIN, - MT9P031_ROW_START_MAX); - rect.width = clamp(ALIGN(crop->rect.width, 2), - MT9P031_WINDOW_WIDTH_MIN, - MT9P031_WINDOW_WIDTH_MAX); - rect.height = clamp(ALIGN(crop->rect.height, 2), - MT9P031_WINDOW_HEIGHT_MIN, - MT9P031_WINDOW_HEIGHT_MAX); - - rect.width = min(rect.width, MT9P031_PIXEL_ARRAY_WIDTH - rect.left); - rect.height = min(rect.height, MT9P031_PIXEL_ARRAY_HEIGHT - rect.top); - - __crop = __mt9p031_get_pad_crop(mt9p031, fh, crop->pad, crop->which); - - if (rect.width != __crop->width || rect.height != __crop->height) { - /* Reset the output image size if the crop rectangle size has - * been modified. - */ - __format = __mt9p031_get_pad_format(mt9p031, fh, crop->pad, - crop->which); - __format->width = rect.width; - __format->height = rect.height; - } - - *__crop = rect; - crop->rect = rect; - - return 0; -} - -/* ----------------------------------------------------------------------------- - * V4L2 subdev control operations - */ - -#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001) -#define V4L2_CID_BLC_AUTO (V4L2_CID_USER_BASE | 0x1002) -#define V4L2_CID_BLC_TARGET_LEVEL (V4L2_CID_USER_BASE | 0x1003) -#define V4L2_CID_BLC_ANALOG_OFFSET (V4L2_CID_USER_BASE | 0x1004) -#define V4L2_CID_BLC_DIGITAL_OFFSET (V4L2_CID_USER_BASE | 0x1005) - -static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9p031 *mt9p031 = - container_of(ctrl->handler, struct mt9p031, ctrls); - struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); - u16 data; - int ret; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - ret = mt9p031_write(client, MT9P031_SHUTTER_WIDTH_UPPER, - (ctrl->val >> 16) & 0xffff); - if (ret < 0) - return ret; - - return mt9p031_write(client, MT9P031_SHUTTER_WIDTH_LOWER, - ctrl->val & 0xffff); - - case V4L2_CID_GAIN: - /* Gain is controlled by 2 analog stages and a digital stage. - * Valid values for the 3 stages are - * - * Stage Min Max Step - * ------------------------------------------ - * First analog stage x1 x2 1 - * Second analog stage x1 x4 0.125 - * Digital stage x1 x16 0.125 - * - * To minimize noise, the gain stages should be used in the - * second analog stage, first analog stage, digital stage order. - * Gain from a previous stage should be pushed to its maximum - * value before the next stage is used. - */ - if (ctrl->val <= 32) { - data = ctrl->val; - } else if (ctrl->val <= 64) { - ctrl->val &= ~1; - data = (1 << 6) | (ctrl->val >> 1); - } else { - ctrl->val &= ~7; - data = ((ctrl->val - 64) << 5) | (1 << 6) | 32; - } - - return mt9p031_write(client, MT9P031_GLOBAL_GAIN, data); - - case V4L2_CID_HFLIP: - if (ctrl->val) - return mt9p031_set_mode2(mt9p031, - 0, MT9P031_READ_MODE_2_COL_MIR); - else - return mt9p031_set_mode2(mt9p031, - MT9P031_READ_MODE_2_COL_MIR, 0); - - case V4L2_CID_VFLIP: - if (ctrl->val) - return mt9p031_set_mode2(mt9p031, - 0, MT9P031_READ_MODE_2_ROW_MIR); - else - return mt9p031_set_mode2(mt9p031, - MT9P031_READ_MODE_2_ROW_MIR, 0); - - case V4L2_CID_TEST_PATTERN: - if (!ctrl->val) { - /* Restore the black level compensation settings. */ - if (mt9p031->blc_auto->cur.val != 0) { - ret = mt9p031_s_ctrl(mt9p031->blc_auto); - if (ret < 0) - return ret; - } - if (mt9p031->blc_offset->cur.val != 0) { - ret = mt9p031_s_ctrl(mt9p031->blc_offset); - if (ret < 0) - return ret; - } - return mt9p031_write(client, MT9P031_TEST_PATTERN, - MT9P031_TEST_PATTERN_DISABLE); - } - - ret = mt9p031_write(client, MT9P031_TEST_PATTERN_GREEN, 0x05a0); - if (ret < 0) - return ret; - ret = mt9p031_write(client, MT9P031_TEST_PATTERN_RED, 0x0a50); - if (ret < 0) - return ret; - ret = mt9p031_write(client, MT9P031_TEST_PATTERN_BLUE, 0x0aa0); - if (ret < 0) - return ret; - - /* Disable digital black level compensation when using a test - * pattern. - */ - ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC, - 0); - if (ret < 0) - return ret; - - ret = mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, 0); - if (ret < 0) - return ret; - - return mt9p031_write(client, MT9P031_TEST_PATTERN, - ((ctrl->val - 1) << MT9P031_TEST_PATTERN_SHIFT) - | MT9P031_TEST_PATTERN_ENABLE); - - case V4L2_CID_BLC_AUTO: - ret = mt9p031_set_mode2(mt9p031, - ctrl->val ? 0 : MT9P031_READ_MODE_2_ROW_BLC, - ctrl->val ? MT9P031_READ_MODE_2_ROW_BLC : 0); - if (ret < 0) - return ret; - - return mt9p031_write(client, MT9P031_BLACK_LEVEL_CALIBRATION, - ctrl->val ? 0 : MT9P031_BLC_MANUAL_BLC); - - case V4L2_CID_BLC_TARGET_LEVEL: - return mt9p031_write(client, MT9P031_ROW_BLACK_TARGET, - ctrl->val); - - case V4L2_CID_BLC_ANALOG_OFFSET: - data = ctrl->val & ((1 << 9) - 1); - - ret = mt9p031_write(client, MT9P031_GREEN1_OFFSET, data); - if (ret < 0) - return ret; - ret = mt9p031_write(client, MT9P031_GREEN2_OFFSET, data); - if (ret < 0) - return ret; - ret = mt9p031_write(client, MT9P031_RED_OFFSET, data); - if (ret < 0) - return ret; - return mt9p031_write(client, MT9P031_BLUE_OFFSET, data); - - case V4L2_CID_BLC_DIGITAL_OFFSET: - return mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, - ctrl->val & ((1 << 12) - 1)); - } - - return 0; -} - -static struct v4l2_ctrl_ops mt9p031_ctrl_ops = { - .s_ctrl = mt9p031_s_ctrl, -}; - -static const char * const mt9p031_test_pattern_menu[] = { - "Disabled", - "Color Field", - "Horizontal Gradient", - "Vertical Gradient", - "Diagonal Gradient", - "Classic Test Pattern", - "Walking 1s", - "Monochrome Horizontal Bars", - "Monochrome Vertical Bars", - "Vertical Color Bars", -}; - -static const struct v4l2_ctrl_config mt9p031_ctrls[] = { - { - .ops = &mt9p031_ctrl_ops, - .id = V4L2_CID_TEST_PATTERN, - .type = V4L2_CTRL_TYPE_MENU, - .name = "Test Pattern", - .min = 0, - .max = ARRAY_SIZE(mt9p031_test_pattern_menu) - 1, - .step = 0, - .def = 0, - .flags = 0, - .menu_skip_mask = 0, - .qmenu = mt9p031_test_pattern_menu, - }, { - .ops = &mt9p031_ctrl_ops, - .id = V4L2_CID_BLC_AUTO, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "BLC, Auto", - .min = 0, - .max = 1, - .step = 1, - .def = 1, - .flags = 0, - }, { - .ops = &mt9p031_ctrl_ops, - .id = V4L2_CID_BLC_TARGET_LEVEL, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "BLC Target Level", - .min = 0, - .max = 4095, - .step = 1, - .def = 168, - .flags = 0, - }, { - .ops = &mt9p031_ctrl_ops, - .id = V4L2_CID_BLC_ANALOG_OFFSET, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "BLC Analog Offset", - .min = -255, - .max = 255, - .step = 1, - .def = 32, - .flags = 0, - }, { - .ops = &mt9p031_ctrl_ops, - .id = V4L2_CID_BLC_DIGITAL_OFFSET, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "BLC Digital Offset", - .min = -2048, - .max = 2047, - .step = 1, - .def = 40, - .flags = 0, - } -}; - -/* ----------------------------------------------------------------------------- - * V4L2 subdev core operations - */ - -static int mt9p031_set_power(struct v4l2_subdev *subdev, int on) -{ - struct mt9p031 *mt9p031 = to_mt9p031(subdev); - int ret = 0; - - mutex_lock(&mt9p031->power_lock); - - /* If the power count is modified from 0 to != 0 or from != 0 to 0, - * update the power state. - */ - if (mt9p031->power_count == !on) { - ret = __mt9p031_set_power(mt9p031, !!on); - if (ret < 0) - goto out; - } - - /* Update the power count. */ - mt9p031->power_count += on ? 1 : -1; - WARN_ON(mt9p031->power_count < 0); - -out: - mutex_unlock(&mt9p031->power_lock); - return ret; -} - -/* ----------------------------------------------------------------------------- - * V4L2 subdev internal operations - */ - -static int mt9p031_registered(struct v4l2_subdev *subdev) -{ - struct i2c_client *client = v4l2_get_subdevdata(subdev); - struct mt9p031 *mt9p031 = to_mt9p031(subdev); - s32 data; - int ret; - - ret = mt9p031_power_on(mt9p031); - if (ret < 0) { - dev_err(&client->dev, "MT9P031 power up failed\n"); - return ret; - } - - /* Read out the chip version register */ - data = mt9p031_read(client, MT9P031_CHIP_VERSION); - if (data != MT9P031_CHIP_VERSION_VALUE) { - dev_err(&client->dev, "MT9P031 not detected, wrong version " - "0x%04x\n", data); - return -ENODEV; - } - - mt9p031_power_off(mt9p031); - - dev_info(&client->dev, "MT9P031 detected at address 0x%02x\n", - client->addr); - - return ret; -} - -static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) -{ - struct mt9p031 *mt9p031 = to_mt9p031(subdev); - struct v4l2_mbus_framefmt *format; - struct v4l2_rect *crop; - - crop = v4l2_subdev_get_try_crop(fh, 0); - crop->left = MT9P031_COLUMN_START_DEF; - crop->top = MT9P031_ROW_START_DEF; - crop->width = MT9P031_WINDOW_WIDTH_DEF; - crop->height = MT9P031_WINDOW_HEIGHT_DEF; - - format = v4l2_subdev_get_try_format(fh, 0); - - if (mt9p031->model == MT9P031_MODEL_MONOCHROME) - format->code = V4L2_MBUS_FMT_Y12_1X12; - else - format->code = V4L2_MBUS_FMT_SGRBG12_1X12; - - format->width = MT9P031_WINDOW_WIDTH_DEF; - format->height = MT9P031_WINDOW_HEIGHT_DEF; - format->field = V4L2_FIELD_NONE; - format->colorspace = V4L2_COLORSPACE_SRGB; - - return mt9p031_set_power(subdev, 1); -} - -static int mt9p031_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) -{ - return mt9p031_set_power(subdev, 0); -} - -static struct v4l2_subdev_core_ops mt9p031_subdev_core_ops = { - .s_power = mt9p031_set_power, -}; - -static struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = { - .s_stream = mt9p031_s_stream, -}; - -static struct v4l2_subdev_pad_ops mt9p031_subdev_pad_ops = { - .enum_mbus_code = mt9p031_enum_mbus_code, - .enum_frame_size = mt9p031_enum_frame_size, - .get_fmt = mt9p031_get_format, - .set_fmt = mt9p031_set_format, - .get_crop = mt9p031_get_crop, - .set_crop = mt9p031_set_crop, -}; - -static struct v4l2_subdev_ops mt9p031_subdev_ops = { - .core = &mt9p031_subdev_core_ops, - .video = &mt9p031_subdev_video_ops, - .pad = &mt9p031_subdev_pad_ops, -}; - -static const struct v4l2_subdev_internal_ops mt9p031_subdev_internal_ops = { - .registered = mt9p031_registered, - .open = mt9p031_open, - .close = mt9p031_close, -}; - -/* ----------------------------------------------------------------------------- - * Driver initialization and probing - */ - -static int mt9p031_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct mt9p031_platform_data *pdata = client->dev.platform_data; - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct mt9p031 *mt9p031; - unsigned int i; - int ret; - - if (pdata == NULL) { - dev_err(&client->dev, "No platform data\n"); - return -EINVAL; - } - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { - dev_warn(&client->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); - return -EIO; - } - - mt9p031 = kzalloc(sizeof(*mt9p031), GFP_KERNEL); - if (mt9p031 == NULL) - return -ENOMEM; - - mt9p031->pdata = pdata; - mt9p031->output_control = MT9P031_OUTPUT_CONTROL_DEF; - mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC; - mt9p031->model = did->driver_data; - mt9p031->reset = -1; - - v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 5); - - v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, - V4L2_CID_EXPOSURE, MT9P031_SHUTTER_WIDTH_MIN, - MT9P031_SHUTTER_WIDTH_MAX, 1, - MT9P031_SHUTTER_WIDTH_DEF); - v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, - V4L2_CID_GAIN, MT9P031_GLOBAL_GAIN_MIN, - MT9P031_GLOBAL_GAIN_MAX, 1, MT9P031_GLOBAL_GAIN_DEF); - v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, - V4L2_CID_PIXEL_RATE, pdata->target_freq, - pdata->target_freq, 1, pdata->target_freq); - - for (i = 0; i < ARRAY_SIZE(mt9p031_ctrls); ++i) - v4l2_ctrl_new_custom(&mt9p031->ctrls, &mt9p031_ctrls[i], NULL); - - mt9p031->subdev.ctrl_handler = &mt9p031->ctrls; - - if (mt9p031->ctrls.error) { - printk(KERN_INFO "%s: control initialization error %d\n", - __func__, mt9p031->ctrls.error); - ret = mt9p031->ctrls.error; - goto done; - } - - mt9p031->blc_auto = v4l2_ctrl_find(&mt9p031->ctrls, V4L2_CID_BLC_AUTO); - mt9p031->blc_offset = v4l2_ctrl_find(&mt9p031->ctrls, - V4L2_CID_BLC_DIGITAL_OFFSET); - - mutex_init(&mt9p031->power_lock); - v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops); - mt9p031->subdev.internal_ops = &mt9p031_subdev_internal_ops; - - mt9p031->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&mt9p031->subdev.entity, 1, &mt9p031->pad, 0); - if (ret < 0) - goto done; - - mt9p031->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - mt9p031->crop.width = MT9P031_WINDOW_WIDTH_DEF; - mt9p031->crop.height = MT9P031_WINDOW_HEIGHT_DEF; - mt9p031->crop.left = MT9P031_COLUMN_START_DEF; - mt9p031->crop.top = MT9P031_ROW_START_DEF; - - if (mt9p031->model == MT9P031_MODEL_MONOCHROME) - mt9p031->format.code = V4L2_MBUS_FMT_Y12_1X12; - else - mt9p031->format.code = V4L2_MBUS_FMT_SGRBG12_1X12; - - mt9p031->format.width = MT9P031_WINDOW_WIDTH_DEF; - mt9p031->format.height = MT9P031_WINDOW_HEIGHT_DEF; - mt9p031->format.field = V4L2_FIELD_NONE; - mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB; - - if (pdata->reset != -1) { - ret = gpio_request_one(pdata->reset, GPIOF_OUT_INIT_LOW, - "mt9p031_rst"); - if (ret < 0) - goto done; - - mt9p031->reset = pdata->reset; - } - - ret = mt9p031_pll_setup(mt9p031); - -done: - if (ret < 0) { - if (mt9p031->reset != -1) - gpio_free(mt9p031->reset); - - v4l2_ctrl_handler_free(&mt9p031->ctrls); - media_entity_cleanup(&mt9p031->subdev.entity); - kfree(mt9p031); - } - - return ret; -} - -static int mt9p031_remove(struct i2c_client *client) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct mt9p031 *mt9p031 = to_mt9p031(subdev); - - v4l2_ctrl_handler_free(&mt9p031->ctrls); - v4l2_device_unregister_subdev(subdev); - media_entity_cleanup(&subdev->entity); - if (mt9p031->reset != -1) - gpio_free(mt9p031->reset); - kfree(mt9p031); - - return 0; -} - -static const struct i2c_device_id mt9p031_id[] = { - { "mt9p031", MT9P031_MODEL_COLOR }, - { "mt9p031m", MT9P031_MODEL_MONOCHROME }, - { } -}; -MODULE_DEVICE_TABLE(i2c, mt9p031_id); - -static struct i2c_driver mt9p031_i2c_driver = { - .driver = { - .name = "mt9p031", - }, - .probe = mt9p031_probe, - .remove = mt9p031_remove, - .id_table = mt9p031_id, -}; - -module_i2c_driver(mt9p031_i2c_driver); - -MODULE_DESCRIPTION("Aptina MT9P031 Camera driver"); -MODULE_AUTHOR("Bastian Hecht "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/mt9t001.c b/drivers/media/video/mt9t001.c deleted file mode 100644 index 6d343adf891..00000000000 --- a/drivers/media/video/mt9t001.c +++ /dev/null @@ -1,833 +0,0 @@ -/* - * Driver for MT9T001 CMOS Image Sensor from Aptina (Micron) - * - * Copyright (C) 2010-2011, Laurent Pinchart - * - * Based on the MT9M001 driver, - * - * Copyright (C) 2008, Guennadi Liakhovetski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define MT9T001_PIXEL_ARRAY_HEIGHT 1568 -#define MT9T001_PIXEL_ARRAY_WIDTH 2112 - -#define MT9T001_CHIP_VERSION 0x00 -#define MT9T001_CHIP_ID 0x1621 -#define MT9T001_ROW_START 0x01 -#define MT9T001_ROW_START_MIN 0 -#define MT9T001_ROW_START_DEF 20 -#define MT9T001_ROW_START_MAX 1534 -#define MT9T001_COLUMN_START 0x02 -#define MT9T001_COLUMN_START_MIN 0 -#define MT9T001_COLUMN_START_DEF 32 -#define MT9T001_COLUMN_START_MAX 2046 -#define MT9T001_WINDOW_HEIGHT 0x03 -#define MT9T001_WINDOW_HEIGHT_MIN 1 -#define MT9T001_WINDOW_HEIGHT_DEF 1535 -#define MT9T001_WINDOW_HEIGHT_MAX 1567 -#define MT9T001_WINDOW_WIDTH 0x04 -#define MT9T001_WINDOW_WIDTH_MIN 1 -#define MT9T001_WINDOW_WIDTH_DEF 2047 -#define MT9T001_WINDOW_WIDTH_MAX 2111 -#define MT9T001_HORIZONTAL_BLANKING 0x05 -#define MT9T001_HORIZONTAL_BLANKING_MIN 21 -#define MT9T001_HORIZONTAL_BLANKING_MAX 1023 -#define MT9T001_VERTICAL_BLANKING 0x06 -#define MT9T001_VERTICAL_BLANKING_MIN 3 -#define MT9T001_VERTICAL_BLANKING_MAX 1023 -#define MT9T001_OUTPUT_CONTROL 0x07 -#define MT9T001_OUTPUT_CONTROL_SYNC (1 << 0) -#define MT9T001_OUTPUT_CONTROL_CHIP_ENABLE (1 << 1) -#define MT9T001_OUTPUT_CONTROL_TEST_DATA (1 << 6) -#define MT9T001_SHUTTER_WIDTH_HIGH 0x08 -#define MT9T001_SHUTTER_WIDTH_LOW 0x09 -#define MT9T001_SHUTTER_WIDTH_MIN 1 -#define MT9T001_SHUTTER_WIDTH_DEF 1561 -#define MT9T001_SHUTTER_WIDTH_MAX (1024 * 1024) -#define MT9T001_PIXEL_CLOCK 0x0a -#define MT9T001_PIXEL_CLOCK_INVERT (1 << 15) -#define MT9T001_PIXEL_CLOCK_SHIFT_MASK (7 << 8) -#define MT9T001_PIXEL_CLOCK_SHIFT_SHIFT 8 -#define MT9T001_PIXEL_CLOCK_DIVIDE_MASK (0x7f << 0) -#define MT9T001_FRAME_RESTART 0x0b -#define MT9T001_SHUTTER_DELAY 0x0c -#define MT9T001_SHUTTER_DELAY_MAX 2047 -#define MT9T001_RESET 0x0d -#define MT9T001_READ_MODE1 0x1e -#define MT9T001_READ_MODE_SNAPSHOT (1 << 8) -#define MT9T001_READ_MODE_STROBE_ENABLE (1 << 9) -#define MT9T001_READ_MODE_STROBE_WIDTH (1 << 10) -#define MT9T001_READ_MODE_STROBE_OVERRIDE (1 << 11) -#define MT9T001_READ_MODE2 0x20 -#define MT9T001_READ_MODE_BAD_FRAMES (1 << 0) -#define MT9T001_READ_MODE_LINE_VALID_CONTINUOUS (1 << 9) -#define MT9T001_READ_MODE_LINE_VALID_FRAME (1 << 10) -#define MT9T001_READ_MODE3 0x21 -#define MT9T001_READ_MODE_GLOBAL_RESET (1 << 0) -#define MT9T001_READ_MODE_GHST_CTL (1 << 1) -#define MT9T001_ROW_ADDRESS_MODE 0x22 -#define MT9T001_ROW_SKIP_MASK (7 << 0) -#define MT9T001_ROW_BIN_MASK (3 << 3) -#define MT9T001_ROW_BIN_SHIFT 3 -#define MT9T001_COLUMN_ADDRESS_MODE 0x23 -#define MT9T001_COLUMN_SKIP_MASK (7 << 0) -#define MT9T001_COLUMN_BIN_MASK (3 << 3) -#define MT9T001_COLUMN_BIN_SHIFT 3 -#define MT9T001_GREEN1_GAIN 0x2b -#define MT9T001_BLUE_GAIN 0x2c -#define MT9T001_RED_GAIN 0x2d -#define MT9T001_GREEN2_GAIN 0x2e -#define MT9T001_TEST_DATA 0x32 -#define MT9T001_GLOBAL_GAIN 0x35 -#define MT9T001_GLOBAL_GAIN_MIN 8 -#define MT9T001_GLOBAL_GAIN_MAX 1024 -#define MT9T001_BLACK_LEVEL 0x49 -#define MT9T001_ROW_BLACK_DEFAULT_OFFSET 0x4b -#define MT9T001_BLC_DELTA_THRESHOLDS 0x5d -#define MT9T001_CAL_THRESHOLDS 0x5f -#define MT9T001_GREEN1_OFFSET 0x60 -#define MT9T001_GREEN2_OFFSET 0x61 -#define MT9T001_BLACK_LEVEL_CALIBRATION 0x62 -#define MT9T001_BLACK_LEVEL_OVERRIDE (1 << 0) -#define MT9T001_BLACK_LEVEL_DISABLE_OFFSET (1 << 1) -#define MT9T001_BLACK_LEVEL_RECALCULATE (1 << 12) -#define MT9T001_BLACK_LEVEL_LOCK_RED_BLUE (1 << 13) -#define MT9T001_BLACK_LEVEL_LOCK_GREEN (1 << 14) -#define MT9T001_RED_OFFSET 0x63 -#define MT9T001_BLUE_OFFSET 0x64 - -struct mt9t001 { - struct v4l2_subdev subdev; - struct media_pad pad; - - struct v4l2_mbus_framefmt format; - struct v4l2_rect crop; - - struct v4l2_ctrl_handler ctrls; - struct v4l2_ctrl *gains[4]; - - u16 output_control; - u16 black_level; -}; - -static inline struct mt9t001 *to_mt9t001(struct v4l2_subdev *sd) -{ - return container_of(sd, struct mt9t001, subdev); -} - -static int mt9t001_read(struct i2c_client *client, u8 reg) -{ - return i2c_smbus_read_word_swapped(client, reg); -} - -static int mt9t001_write(struct i2c_client *client, u8 reg, u16 data) -{ - return i2c_smbus_write_word_swapped(client, reg, data); -} - -static int mt9t001_set_output_control(struct mt9t001 *mt9t001, u16 clear, - u16 set) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev); - u16 value = (mt9t001->output_control & ~clear) | set; - int ret; - - if (value == mt9t001->output_control) - return 0; - - ret = mt9t001_write(client, MT9T001_OUTPUT_CONTROL, value); - if (ret < 0) - return ret; - - mt9t001->output_control = value; - return 0; -} - -/* ----------------------------------------------------------------------------- - * V4L2 subdev video operations - */ - -static struct v4l2_mbus_framefmt * -__mt9t001_get_pad_format(struct mt9t001 *mt9t001, struct v4l2_subdev_fh *fh, - unsigned int pad, enum v4l2_subdev_format_whence which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(fh, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &mt9t001->format; - default: - return NULL; - } -} - -static struct v4l2_rect * -__mt9t001_get_pad_crop(struct mt9t001 *mt9t001, struct v4l2_subdev_fh *fh, - unsigned int pad, enum v4l2_subdev_format_whence which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(fh, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &mt9t001->crop; - default: - return NULL; - } -} - -static int mt9t001_s_stream(struct v4l2_subdev *subdev, int enable) -{ - const u16 mode = MT9T001_OUTPUT_CONTROL_CHIP_ENABLE; - struct i2c_client *client = v4l2_get_subdevdata(subdev); - struct mt9t001 *mt9t001 = to_mt9t001(subdev); - struct v4l2_mbus_framefmt *format = &mt9t001->format; - struct v4l2_rect *crop = &mt9t001->crop; - unsigned int hratio; - unsigned int vratio; - int ret; - - if (!enable) - return mt9t001_set_output_control(mt9t001, mode, 0); - - /* Configure the window size and row/column bin */ - hratio = DIV_ROUND_CLOSEST(crop->width, format->width); - vratio = DIV_ROUND_CLOSEST(crop->height, format->height); - - ret = mt9t001_write(client, MT9T001_ROW_ADDRESS_MODE, hratio - 1); - if (ret < 0) - return ret; - - ret = mt9t001_write(client, MT9T001_COLUMN_ADDRESS_MODE, vratio - 1); - if (ret < 0) - return ret; - - ret = mt9t001_write(client, MT9T001_COLUMN_START, crop->left); - if (ret < 0) - return ret; - - ret = mt9t001_write(client, MT9T001_ROW_START, crop->top); - if (ret < 0) - return ret; - - ret = mt9t001_write(client, MT9T001_WINDOW_WIDTH, crop->width - 1); - if (ret < 0) - return ret; - - ret = mt9t001_write(client, MT9T001_WINDOW_HEIGHT, crop->height - 1); - if (ret < 0) - return ret; - - /* Switch to master "normal" mode */ - return mt9t001_set_output_control(mt9t001, 0, mode); -} - -static int mt9t001_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->index > 0) - return -EINVAL; - - code->code = V4L2_MBUS_FMT_SGRBG10_1X10; - return 0; -} - -static int mt9t001_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_frame_size_enum *fse) -{ - if (fse->index >= 8 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10) - return -EINVAL; - - fse->min_width = (MT9T001_WINDOW_WIDTH_DEF + 1) / fse->index; - fse->max_width = fse->min_width; - fse->min_height = (MT9T001_WINDOW_HEIGHT_DEF + 1) / fse->index; - fse->max_height = fse->min_height; - - return 0; -} - -static int mt9t001_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *format) -{ - struct mt9t001 *mt9t001 = to_mt9t001(subdev); - - format->format = *__mt9t001_get_pad_format(mt9t001, fh, format->pad, - format->which); - return 0; -} - -static int mt9t001_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *format) -{ - struct mt9t001 *mt9t001 = to_mt9t001(subdev); - struct v4l2_mbus_framefmt *__format; - struct v4l2_rect *__crop; - unsigned int width; - unsigned int height; - unsigned int hratio; - unsigned int vratio; - - __crop = __mt9t001_get_pad_crop(mt9t001, fh, format->pad, - format->which); - - /* Clamp the width and height to avoid dividing by zero. */ - width = clamp_t(unsigned int, ALIGN(format->format.width, 2), - max(__crop->width / 8, MT9T001_WINDOW_HEIGHT_MIN + 1), - __crop->width); - height = clamp_t(unsigned int, ALIGN(format->format.height, 2), - max(__crop->height / 8, MT9T001_WINDOW_HEIGHT_MIN + 1), - __crop->height); - - hratio = DIV_ROUND_CLOSEST(__crop->width, width); - vratio = DIV_ROUND_CLOSEST(__crop->height, height); - - __format = __mt9t001_get_pad_format(mt9t001, fh, format->pad, - format->which); - __format->width = __crop->width / hratio; - __format->height = __crop->height / vratio; - - format->format = *__format; - - return 0; -} - -static int mt9t001_get_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) -{ - struct mt9t001 *mt9t001 = to_mt9t001(subdev); - - crop->rect = *__mt9t001_get_pad_crop(mt9t001, fh, crop->pad, - crop->which); - return 0; -} - -static int mt9t001_set_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) -{ - struct mt9t001 *mt9t001 = to_mt9t001(subdev); - struct v4l2_mbus_framefmt *__format; - struct v4l2_rect *__crop; - struct v4l2_rect rect; - - /* Clamp the crop rectangle boundaries and align them to a multiple of 2 - * pixels. - */ - rect.left = clamp(ALIGN(crop->rect.left, 2), - MT9T001_COLUMN_START_MIN, - MT9T001_COLUMN_START_MAX); - rect.top = clamp(ALIGN(crop->rect.top, 2), - MT9T001_ROW_START_MIN, - MT9T001_ROW_START_MAX); - rect.width = clamp(ALIGN(crop->rect.width, 2), - MT9T001_WINDOW_WIDTH_MIN + 1, - MT9T001_WINDOW_WIDTH_MAX + 1); - rect.height = clamp(ALIGN(crop->rect.height, 2), - MT9T001_WINDOW_HEIGHT_MIN + 1, - MT9T001_WINDOW_HEIGHT_MAX + 1); - - rect.width = min(rect.width, MT9T001_PIXEL_ARRAY_WIDTH - rect.left); - rect.height = min(rect.height, MT9T001_PIXEL_ARRAY_HEIGHT - rect.top); - - __crop = __mt9t001_get_pad_crop(mt9t001, fh, crop->pad, crop->which); - - if (rect.width != __crop->width || rect.height != __crop->height) { - /* Reset the output image size if the crop rectangle size has - * been modified. - */ - __format = __mt9t001_get_pad_format(mt9t001, fh, crop->pad, - crop->which); - __format->width = rect.width; - __format->height = rect.height; - } - - *__crop = rect; - crop->rect = rect; - - return 0; -} - -/* ----------------------------------------------------------------------------- - * V4L2 subdev control operations - */ - -#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001) -#define V4L2_CID_BLACK_LEVEL_AUTO (V4L2_CID_USER_BASE | 0x1002) -#define V4L2_CID_BLACK_LEVEL_OFFSET (V4L2_CID_USER_BASE | 0x1003) -#define V4L2_CID_BLACK_LEVEL_CALIBRATE (V4L2_CID_USER_BASE | 0x1004) - -#define V4L2_CID_GAIN_RED (V4L2_CTRL_CLASS_CAMERA | 0x1001) -#define V4L2_CID_GAIN_GREEN_RED (V4L2_CTRL_CLASS_CAMERA | 0x1002) -#define V4L2_CID_GAIN_GREEN_BLUE (V4L2_CTRL_CLASS_CAMERA | 0x1003) -#define V4L2_CID_GAIN_BLUE (V4L2_CTRL_CLASS_CAMERA | 0x1004) - -static u16 mt9t001_gain_value(s32 *gain) -{ - /* Gain is controlled by 2 analog stages and a digital stage. Valid - * values for the 3 stages are - * - * Stage Min Max Step - * ------------------------------------------ - * First analog stage x1 x2 1 - * Second analog stage x1 x4 0.125 - * Digital stage x1 x16 0.125 - * - * To minimize noise, the gain stages should be used in the second - * analog stage, first analog stage, digital stage order. Gain from a - * previous stage should be pushed to its maximum value before the next - * stage is used. - */ - if (*gain <= 32) - return *gain; - - if (*gain <= 64) { - *gain &= ~1; - return (1 << 6) | (*gain >> 1); - } - - *gain &= ~7; - return ((*gain - 64) << 5) | (1 << 6) | 32; -} - -static int mt9t001_ctrl_freeze(struct mt9t001 *mt9t001, bool freeze) -{ - return mt9t001_set_output_control(mt9t001, - freeze ? 0 : MT9T001_OUTPUT_CONTROL_SYNC, - freeze ? MT9T001_OUTPUT_CONTROL_SYNC : 0); -} - -static int mt9t001_s_ctrl(struct v4l2_ctrl *ctrl) -{ - static const u8 gains[4] = { - MT9T001_RED_GAIN, MT9T001_GREEN1_GAIN, - MT9T001_GREEN2_GAIN, MT9T001_BLUE_GAIN - }; - - struct mt9t001 *mt9t001 = - container_of(ctrl->handler, struct mt9t001, ctrls); - struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev); - unsigned int count; - unsigned int i; - u16 value; - int ret; - - switch (ctrl->id) { - case V4L2_CID_GAIN_RED: - case V4L2_CID_GAIN_GREEN_RED: - case V4L2_CID_GAIN_GREEN_BLUE: - case V4L2_CID_GAIN_BLUE: - - /* Disable control updates if more than one control has changed - * in the cluster. - */ - for (i = 0, count = 0; i < 4; ++i) { - struct v4l2_ctrl *gain = mt9t001->gains[i]; - - if (gain->val != gain->cur.val) - count++; - } - - if (count > 1) { - ret = mt9t001_ctrl_freeze(mt9t001, true); - if (ret < 0) - return ret; - } - - /* Update the gain controls. */ - for (i = 0; i < 4; ++i) { - struct v4l2_ctrl *gain = mt9t001->gains[i]; - - if (gain->val == gain->cur.val) - continue; - - value = mt9t001_gain_value(&gain->val); - ret = mt9t001_write(client, gains[i], value); - if (ret < 0) { - mt9t001_ctrl_freeze(mt9t001, false); - return ret; - } - } - - /* Enable control updates. */ - if (count > 1) { - ret = mt9t001_ctrl_freeze(mt9t001, false); - if (ret < 0) - return ret; - } - - break; - - case V4L2_CID_EXPOSURE: - ret = mt9t001_write(client, MT9T001_SHUTTER_WIDTH_LOW, - ctrl->val & 0xffff); - if (ret < 0) - return ret; - - return mt9t001_write(client, MT9T001_SHUTTER_WIDTH_HIGH, - ctrl->val >> 16); - - case V4L2_CID_TEST_PATTERN: - ret = mt9t001_set_output_control(mt9t001, - ctrl->val ? 0 : MT9T001_OUTPUT_CONTROL_TEST_DATA, - ctrl->val ? MT9T001_OUTPUT_CONTROL_TEST_DATA : 0); - if (ret < 0) - return ret; - - return mt9t001_write(client, MT9T001_TEST_DATA, ctrl->val << 2); - - case V4L2_CID_BLACK_LEVEL_AUTO: - value = ctrl->val ? 0 : MT9T001_BLACK_LEVEL_OVERRIDE; - ret = mt9t001_write(client, MT9T001_BLACK_LEVEL_CALIBRATION, - value); - if (ret < 0) - return ret; - - mt9t001->black_level = value; - break; - - case V4L2_CID_BLACK_LEVEL_OFFSET: - ret = mt9t001_write(client, MT9T001_GREEN1_OFFSET, ctrl->val); - if (ret < 0) - return ret; - - ret = mt9t001_write(client, MT9T001_GREEN2_OFFSET, ctrl->val); - if (ret < 0) - return ret; - - ret = mt9t001_write(client, MT9T001_RED_OFFSET, ctrl->val); - if (ret < 0) - return ret; - - return mt9t001_write(client, MT9T001_BLUE_OFFSET, ctrl->val); - - case V4L2_CID_BLACK_LEVEL_CALIBRATE: - return mt9t001_write(client, MT9T001_BLACK_LEVEL_CALIBRATION, - MT9T001_BLACK_LEVEL_RECALCULATE | - mt9t001->black_level); - } - - return 0; -} - -static struct v4l2_ctrl_ops mt9t001_ctrl_ops = { - .s_ctrl = mt9t001_s_ctrl, -}; - -static const struct v4l2_ctrl_config mt9t001_ctrls[] = { - { - .ops = &mt9t001_ctrl_ops, - .id = V4L2_CID_TEST_PATTERN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Test pattern", - .min = 0, - .max = 1023, - .step = 1, - .def = 0, - .flags = 0, - }, { - .ops = &mt9t001_ctrl_ops, - .id = V4L2_CID_BLACK_LEVEL_AUTO, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Black Level, Auto", - .min = 0, - .max = 1, - .step = 1, - .def = 1, - .flags = 0, - }, { - .ops = &mt9t001_ctrl_ops, - .id = V4L2_CID_BLACK_LEVEL_OFFSET, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Black Level, Offset", - .min = -256, - .max = 255, - .step = 1, - .def = 32, - .flags = 0, - }, { - .ops = &mt9t001_ctrl_ops, - .id = V4L2_CID_BLACK_LEVEL_CALIBRATE, - .type = V4L2_CTRL_TYPE_BUTTON, - .name = "Black Level, Calibrate", - .min = 0, - .max = 0, - .step = 0, - .def = 0, - .flags = V4L2_CTRL_FLAG_WRITE_ONLY, - }, -}; - -static const struct v4l2_ctrl_config mt9t001_gains[] = { - { - .ops = &mt9t001_ctrl_ops, - .id = V4L2_CID_GAIN_RED, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain, Red", - .min = MT9T001_GLOBAL_GAIN_MIN, - .max = MT9T001_GLOBAL_GAIN_MAX, - .step = 1, - .def = MT9T001_GLOBAL_GAIN_MIN, - .flags = 0, - }, { - .ops = &mt9t001_ctrl_ops, - .id = V4L2_CID_GAIN_GREEN_RED, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain, Green (R)", - .min = MT9T001_GLOBAL_GAIN_MIN, - .max = MT9T001_GLOBAL_GAIN_MAX, - .step = 1, - .def = MT9T001_GLOBAL_GAIN_MIN, - .flags = 0, - }, { - .ops = &mt9t001_ctrl_ops, - .id = V4L2_CID_GAIN_GREEN_BLUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain, Green (B)", - .min = MT9T001_GLOBAL_GAIN_MIN, - .max = MT9T001_GLOBAL_GAIN_MAX, - .step = 1, - .def = MT9T001_GLOBAL_GAIN_MIN, - .flags = 0, - }, { - .ops = &mt9t001_ctrl_ops, - .id = V4L2_CID_GAIN_BLUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain, Blue", - .min = MT9T001_GLOBAL_GAIN_MIN, - .max = MT9T001_GLOBAL_GAIN_MAX, - .step = 1, - .def = MT9T001_GLOBAL_GAIN_MIN, - .flags = 0, - }, -}; - -/* ----------------------------------------------------------------------------- - * V4L2 subdev internal operations - */ - -static int mt9t001_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) -{ - struct v4l2_mbus_framefmt *format; - struct v4l2_rect *crop; - - crop = v4l2_subdev_get_try_crop(fh, 0); - crop->left = MT9T001_COLUMN_START_DEF; - crop->top = MT9T001_ROW_START_DEF; - crop->width = MT9T001_WINDOW_WIDTH_DEF + 1; - crop->height = MT9T001_WINDOW_HEIGHT_DEF + 1; - - format = v4l2_subdev_get_try_format(fh, 0); - format->code = V4L2_MBUS_FMT_SGRBG10_1X10; - format->width = MT9T001_WINDOW_WIDTH_DEF + 1; - format->height = MT9T001_WINDOW_HEIGHT_DEF + 1; - format->field = V4L2_FIELD_NONE; - format->colorspace = V4L2_COLORSPACE_SRGB; - - return 0; -} - -static struct v4l2_subdev_video_ops mt9t001_subdev_video_ops = { - .s_stream = mt9t001_s_stream, -}; - -static struct v4l2_subdev_pad_ops mt9t001_subdev_pad_ops = { - .enum_mbus_code = mt9t001_enum_mbus_code, - .enum_frame_size = mt9t001_enum_frame_size, - .get_fmt = mt9t001_get_format, - .set_fmt = mt9t001_set_format, - .get_crop = mt9t001_get_crop, - .set_crop = mt9t001_set_crop, -}; - -static struct v4l2_subdev_ops mt9t001_subdev_ops = { - .video = &mt9t001_subdev_video_ops, - .pad = &mt9t001_subdev_pad_ops, -}; - -static struct v4l2_subdev_internal_ops mt9t001_subdev_internal_ops = { - .open = mt9t001_open, -}; - -static int mt9t001_video_probe(struct i2c_client *client) -{ - struct mt9t001_platform_data *pdata = client->dev.platform_data; - s32 data; - int ret; - - dev_info(&client->dev, "Probing MT9T001 at address 0x%02x\n", - client->addr); - - /* Reset the chip and stop data read out */ - ret = mt9t001_write(client, MT9T001_RESET, 1); - if (ret < 0) - return ret; - - ret = mt9t001_write(client, MT9T001_RESET, 0); - if (ret < 0) - return ret; - - ret = mt9t001_write(client, MT9T001_OUTPUT_CONTROL, 0); - if (ret < 0) - return ret; - - /* Configure the pixel clock polarity */ - if (pdata->clk_pol) { - ret = mt9t001_write(client, MT9T001_PIXEL_CLOCK, - MT9T001_PIXEL_CLOCK_INVERT); - if (ret < 0) - return ret; - } - - /* Read and check the sensor version */ - data = mt9t001_read(client, MT9T001_CHIP_VERSION); - if (data != MT9T001_CHIP_ID) { - dev_err(&client->dev, "MT9T001 not detected, wrong version " - "0x%04x\n", data); - return -ENODEV; - } - - dev_info(&client->dev, "MT9T001 detected at address 0x%02x\n", - client->addr); - - return ret; -} - -static int mt9t001_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct mt9t001_platform_data *pdata = client->dev.platform_data; - struct mt9t001 *mt9t001; - unsigned int i; - int ret; - - if (pdata == NULL) { - dev_err(&client->dev, "No platform data\n"); - return -EINVAL; - } - - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_WORD_DATA)) { - dev_warn(&client->adapter->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); - return -EIO; - } - - ret = mt9t001_video_probe(client); - if (ret < 0) - return ret; - - mt9t001 = kzalloc(sizeof(*mt9t001), GFP_KERNEL); - if (!mt9t001) - return -ENOMEM; - - v4l2_ctrl_handler_init(&mt9t001->ctrls, ARRAY_SIZE(mt9t001_ctrls) + - ARRAY_SIZE(mt9t001_gains) + 3); - - v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, - V4L2_CID_EXPOSURE, MT9T001_SHUTTER_WIDTH_MIN, - MT9T001_SHUTTER_WIDTH_MAX, 1, - MT9T001_SHUTTER_WIDTH_DEF); - v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, - V4L2_CID_BLACK_LEVEL, 1, 1, 1, 1); - v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, - V4L2_CID_PIXEL_RATE, pdata->ext_clk, pdata->ext_clk, - 1, pdata->ext_clk); - - for (i = 0; i < ARRAY_SIZE(mt9t001_ctrls); ++i) - v4l2_ctrl_new_custom(&mt9t001->ctrls, &mt9t001_ctrls[i], NULL); - - for (i = 0; i < ARRAY_SIZE(mt9t001_gains); ++i) - mt9t001->gains[i] = v4l2_ctrl_new_custom(&mt9t001->ctrls, - &mt9t001_gains[i], NULL); - - v4l2_ctrl_cluster(ARRAY_SIZE(mt9t001_gains), mt9t001->gains); - - mt9t001->subdev.ctrl_handler = &mt9t001->ctrls; - - if (mt9t001->ctrls.error) { - printk(KERN_INFO "%s: control initialization error %d\n", - __func__, mt9t001->ctrls.error); - ret = -EINVAL; - goto done; - } - - mt9t001->crop.left = MT9T001_COLUMN_START_DEF; - mt9t001->crop.top = MT9T001_ROW_START_DEF; - mt9t001->crop.width = MT9T001_WINDOW_WIDTH_DEF + 1; - mt9t001->crop.height = MT9T001_WINDOW_HEIGHT_DEF + 1; - - mt9t001->format.code = V4L2_MBUS_FMT_SGRBG10_1X10; - mt9t001->format.width = MT9T001_WINDOW_WIDTH_DEF + 1; - mt9t001->format.height = MT9T001_WINDOW_HEIGHT_DEF + 1; - mt9t001->format.field = V4L2_FIELD_NONE; - mt9t001->format.colorspace = V4L2_COLORSPACE_SRGB; - - v4l2_i2c_subdev_init(&mt9t001->subdev, client, &mt9t001_subdev_ops); - mt9t001->subdev.internal_ops = &mt9t001_subdev_internal_ops; - mt9t001->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - mt9t001->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&mt9t001->subdev.entity, 1, &mt9t001->pad, 0); - -done: - if (ret < 0) { - v4l2_ctrl_handler_free(&mt9t001->ctrls); - media_entity_cleanup(&mt9t001->subdev.entity); - kfree(mt9t001); - } - - return ret; -} - -static int mt9t001_remove(struct i2c_client *client) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct mt9t001 *mt9t001 = to_mt9t001(subdev); - - v4l2_ctrl_handler_free(&mt9t001->ctrls); - v4l2_device_unregister_subdev(subdev); - media_entity_cleanup(&subdev->entity); - kfree(mt9t001); - return 0; -} - -static const struct i2c_device_id mt9t001_id[] = { - { "mt9t001", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, mt9t001_id); - -static struct i2c_driver mt9t001_driver = { - .driver = { - .name = "mt9t001", - }, - .probe = mt9t001_probe, - .remove = mt9t001_remove, - .id_table = mt9t001_id, -}; - -module_i2c_driver(mt9t001_driver); - -MODULE_DESCRIPTION("Aptina (Micron) MT9T001 Camera driver"); -MODULE_AUTHOR("Laurent Pinchart "); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/mt9v011.c b/drivers/media/video/mt9v011.c deleted file mode 100644 index 6bf01ad6276..00000000000 --- a/drivers/media/video/mt9v011.c +++ /dev/null @@ -1,712 +0,0 @@ -/* - * mt9v011 -Micron 1/4-Inch VGA Digital Image Sensor - * - * Copyright (c) 2009 Mauro Carvalho Chehab (mchehab@redhat.com) - * This code is placed under the terms of the GNU General Public License v2 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("Micron mt9v011 sensor driver"); -MODULE_AUTHOR("Mauro Carvalho Chehab "); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-2)"); - -#define R00_MT9V011_CHIP_VERSION 0x00 -#define R01_MT9V011_ROWSTART 0x01 -#define R02_MT9V011_COLSTART 0x02 -#define R03_MT9V011_HEIGHT 0x03 -#define R04_MT9V011_WIDTH 0x04 -#define R05_MT9V011_HBLANK 0x05 -#define R06_MT9V011_VBLANK 0x06 -#define R07_MT9V011_OUT_CTRL 0x07 -#define R09_MT9V011_SHUTTER_WIDTH 0x09 -#define R0A_MT9V011_CLK_SPEED 0x0a -#define R0B_MT9V011_RESTART 0x0b -#define R0C_MT9V011_SHUTTER_DELAY 0x0c -#define R0D_MT9V011_RESET 0x0d -#define R1E_MT9V011_DIGITAL_ZOOM 0x1e -#define R20_MT9V011_READ_MODE 0x20 -#define R2B_MT9V011_GREEN_1_GAIN 0x2b -#define R2C_MT9V011_BLUE_GAIN 0x2c -#define R2D_MT9V011_RED_GAIN 0x2d -#define R2E_MT9V011_GREEN_2_GAIN 0x2e -#define R35_MT9V011_GLOBAL_GAIN 0x35 -#define RF1_MT9V011_CHIP_ENABLE 0xf1 - -#define MT9V011_VERSION 0x8232 -#define MT9V011_REV_B_VERSION 0x8243 - -/* supported controls */ -static struct v4l2_queryctrl mt9v011_qctrl[] = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 0, - .maximum = (1 << 12) - 1 - 0x0020, - .step = 1, - .default_value = 0x0020, - .flags = 0, - }, { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 0, - .maximum = 2047, - .step = 1, - .default_value = 0x01fc, - .flags = 0, - }, { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Red Balance", - .minimum = -1 << 9, - .maximum = (1 << 9) - 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Blue Balance", - .minimum = -1 << 9, - .maximum = (1 << 9) - 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Vflip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, { - } -}; - -struct mt9v011 { - struct v4l2_subdev sd; - unsigned width, height; - unsigned xtal; - unsigned hflip:1; - unsigned vflip:1; - - u16 global_gain, exposure; - s16 red_bal, blue_bal; -}; - -static inline struct mt9v011 *to_mt9v011(struct v4l2_subdev *sd) -{ - return container_of(sd, struct mt9v011, sd); -} - -static int mt9v011_read(struct v4l2_subdev *sd, unsigned char addr) -{ - struct i2c_client *c = v4l2_get_subdevdata(sd); - __be16 buffer; - int rc, val; - - rc = i2c_master_send(c, &addr, 1); - if (rc != 1) - v4l2_dbg(0, debug, sd, - "i2c i/o error: rc == %d (should be 1)\n", rc); - - msleep(10); - - rc = i2c_master_recv(c, (char *)&buffer, 2); - if (rc != 2) - v4l2_dbg(0, debug, sd, - "i2c i/o error: rc == %d (should be 2)\n", rc); - - val = be16_to_cpu(buffer); - - v4l2_dbg(2, debug, sd, "mt9v011: read 0x%02x = 0x%04x\n", addr, val); - - return val; -} - -static void mt9v011_write(struct v4l2_subdev *sd, unsigned char addr, - u16 value) -{ - struct i2c_client *c = v4l2_get_subdevdata(sd); - unsigned char buffer[3]; - int rc; - - buffer[0] = addr; - buffer[1] = value >> 8; - buffer[2] = value & 0xff; - - v4l2_dbg(2, debug, sd, - "mt9v011: writing 0x%02x 0x%04x\n", buffer[0], value); - rc = i2c_master_send(c, buffer, 3); - if (rc != 3) - v4l2_dbg(0, debug, sd, - "i2c i/o error: rc == %d (should be 3)\n", rc); -} - - -struct i2c_reg_value { - unsigned char reg; - u16 value; -}; - -/* - * Values used at the original driver - * Some values are marked as Reserved at the datasheet - */ -static const struct i2c_reg_value mt9v011_init_default[] = { - { R0D_MT9V011_RESET, 0x0001 }, - { R0D_MT9V011_RESET, 0x0000 }, - - { R0C_MT9V011_SHUTTER_DELAY, 0x0000 }, - { R09_MT9V011_SHUTTER_WIDTH, 0x1fc }, - - { R0A_MT9V011_CLK_SPEED, 0x0000 }, - { R1E_MT9V011_DIGITAL_ZOOM, 0x0000 }, - - { R07_MT9V011_OUT_CTRL, 0x0002 }, /* chip enable */ -}; - - -static u16 calc_mt9v011_gain(s16 lineargain) -{ - - u16 digitalgain = 0; - u16 analogmult = 0; - u16 analoginit = 0; - - if (lineargain < 0) - lineargain = 0; - - /* recommended minimum */ - lineargain += 0x0020; - - if (lineargain > 2047) - lineargain = 2047; - - if (lineargain > 1023) { - digitalgain = 3; - analogmult = 3; - analoginit = lineargain / 16; - } else if (lineargain > 511) { - digitalgain = 1; - analogmult = 3; - analoginit = lineargain / 8; - } else if (lineargain > 255) { - analogmult = 3; - analoginit = lineargain / 4; - } else if (lineargain > 127) { - analogmult = 1; - analoginit = lineargain / 2; - } else - analoginit = lineargain; - - return analoginit + (analogmult << 7) + (digitalgain << 9); - -} - -static void set_balance(struct v4l2_subdev *sd) -{ - struct mt9v011 *core = to_mt9v011(sd); - u16 green_gain, blue_gain, red_gain; - u16 exposure; - s16 bal; - - exposure = core->exposure; - - green_gain = calc_mt9v011_gain(core->global_gain); - - bal = core->global_gain; - bal += (core->blue_bal * core->global_gain / (1 << 7)); - blue_gain = calc_mt9v011_gain(bal); - - bal = core->global_gain; - bal += (core->red_bal * core->global_gain / (1 << 7)); - red_gain = calc_mt9v011_gain(bal); - - mt9v011_write(sd, R2B_MT9V011_GREEN_1_GAIN, green_gain); - mt9v011_write(sd, R2E_MT9V011_GREEN_2_GAIN, green_gain); - mt9v011_write(sd, R2C_MT9V011_BLUE_GAIN, blue_gain); - mt9v011_write(sd, R2D_MT9V011_RED_GAIN, red_gain); - mt9v011_write(sd, R09_MT9V011_SHUTTER_WIDTH, exposure); -} - -static void calc_fps(struct v4l2_subdev *sd, u32 *numerator, u32 *denominator) -{ - struct mt9v011 *core = to_mt9v011(sd); - unsigned height, width, hblank, vblank, speed; - unsigned row_time, t_time; - u64 frames_per_ms; - unsigned tmp; - - height = mt9v011_read(sd, R03_MT9V011_HEIGHT); - width = mt9v011_read(sd, R04_MT9V011_WIDTH); - hblank = mt9v011_read(sd, R05_MT9V011_HBLANK); - vblank = mt9v011_read(sd, R06_MT9V011_VBLANK); - speed = mt9v011_read(sd, R0A_MT9V011_CLK_SPEED); - - row_time = (width + 113 + hblank) * (speed + 2); - t_time = row_time * (height + vblank + 1); - - frames_per_ms = core->xtal * 1000l; - do_div(frames_per_ms, t_time); - tmp = frames_per_ms; - - v4l2_dbg(1, debug, sd, "Programmed to %u.%03u fps (%d pixel clcks)\n", - tmp / 1000, tmp % 1000, t_time); - - if (numerator && denominator) { - *numerator = 1000; - *denominator = (u32)frames_per_ms; - } -} - -static u16 calc_speed(struct v4l2_subdev *sd, u32 numerator, u32 denominator) -{ - struct mt9v011 *core = to_mt9v011(sd); - unsigned height, width, hblank, vblank; - unsigned row_time, line_time; - u64 t_time, speed; - - /* Avoid bogus calculus */ - if (!numerator || !denominator) - return 0; - - height = mt9v011_read(sd, R03_MT9V011_HEIGHT); - width = mt9v011_read(sd, R04_MT9V011_WIDTH); - hblank = mt9v011_read(sd, R05_MT9V011_HBLANK); - vblank = mt9v011_read(sd, R06_MT9V011_VBLANK); - - row_time = width + 113 + hblank; - line_time = height + vblank + 1; - - t_time = core->xtal * ((u64)numerator); - /* round to the closest value */ - t_time += denominator / 2; - do_div(t_time, denominator); - - speed = t_time; - do_div(speed, row_time * line_time); - - /* Avoid having a negative value for speed */ - if (speed < 2) - speed = 0; - else - speed -= 2; - - /* Avoid speed overflow */ - if (speed > 15) - return 15; - - return (u16)speed; -} - -static void set_res(struct v4l2_subdev *sd) -{ - struct mt9v011 *core = to_mt9v011(sd); - unsigned vstart, hstart; - - /* - * The mt9v011 doesn't have scaling. So, in order to select the desired - * resolution, we're cropping at the middle of the sensor. - * hblank and vblank should be adjusted, in order to warrant that - * we'll preserve the line timings for 30 fps, no matter what resolution - * is selected. - * NOTE: datasheet says that width (and height) should be filled with - * width-1. However, this doesn't work, since one pixel per line will - * be missing. - */ - - hstart = 20 + (640 - core->width) / 2; - mt9v011_write(sd, R02_MT9V011_COLSTART, hstart); - mt9v011_write(sd, R04_MT9V011_WIDTH, core->width); - mt9v011_write(sd, R05_MT9V011_HBLANK, 771 - core->width); - - vstart = 8 + (480 - core->height) / 2; - mt9v011_write(sd, R01_MT9V011_ROWSTART, vstart); - mt9v011_write(sd, R03_MT9V011_HEIGHT, core->height); - mt9v011_write(sd, R06_MT9V011_VBLANK, 508 - core->height); - - calc_fps(sd, NULL, NULL); -}; - -static void set_read_mode(struct v4l2_subdev *sd) -{ - struct mt9v011 *core = to_mt9v011(sd); - unsigned mode = 0x1000; - - if (core->hflip) - mode |= 0x4000; - - if (core->vflip) - mode |= 0x8000; - - mt9v011_write(sd, R20_MT9V011_READ_MODE, mode); -} - -static int mt9v011_reset(struct v4l2_subdev *sd, u32 val) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(mt9v011_init_default); i++) - mt9v011_write(sd, mt9v011_init_default[i].reg, - mt9v011_init_default[i].value); - - set_balance(sd); - set_res(sd); - set_read_mode(sd); - - return 0; -}; - -static int mt9v011_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct mt9v011 *core = to_mt9v011(sd); - - v4l2_dbg(1, debug, sd, "g_ctrl called\n"); - - switch (ctrl->id) { - case V4L2_CID_GAIN: - ctrl->value = core->global_gain; - return 0; - case V4L2_CID_EXPOSURE: - ctrl->value = core->exposure; - return 0; - case V4L2_CID_RED_BALANCE: - ctrl->value = core->red_bal; - return 0; - case V4L2_CID_BLUE_BALANCE: - ctrl->value = core->blue_bal; - return 0; - case V4L2_CID_HFLIP: - ctrl->value = core->hflip ? 1 : 0; - return 0; - case V4L2_CID_VFLIP: - ctrl->value = core->vflip ? 1 : 0; - return 0; - } - return -EINVAL; -} - -static int mt9v011_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - int i; - - v4l2_dbg(1, debug, sd, "queryctrl called\n"); - - for (i = 0; i < ARRAY_SIZE(mt9v011_qctrl); i++) - if (qc->id && qc->id == mt9v011_qctrl[i].id) { - memcpy(qc, &(mt9v011_qctrl[i]), - sizeof(*qc)); - return 0; - } - - return -EINVAL; -} - - -static int mt9v011_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct mt9v011 *core = to_mt9v011(sd); - u8 i, n; - n = ARRAY_SIZE(mt9v011_qctrl); - - for (i = 0; i < n; i++) { - if (ctrl->id != mt9v011_qctrl[i].id) - continue; - if (ctrl->value < mt9v011_qctrl[i].minimum || - ctrl->value > mt9v011_qctrl[i].maximum) - return -ERANGE; - v4l2_dbg(1, debug, sd, "s_ctrl: id=%d, value=%d\n", - ctrl->id, ctrl->value); - break; - } - - switch (ctrl->id) { - case V4L2_CID_GAIN: - core->global_gain = ctrl->value; - break; - case V4L2_CID_EXPOSURE: - core->exposure = ctrl->value; - break; - case V4L2_CID_RED_BALANCE: - core->red_bal = ctrl->value; - break; - case V4L2_CID_BLUE_BALANCE: - core->blue_bal = ctrl->value; - break; - case V4L2_CID_HFLIP: - core->hflip = ctrl->value; - set_read_mode(sd); - return 0; - case V4L2_CID_VFLIP: - core->vflip = ctrl->value; - set_read_mode(sd); - return 0; - default: - return -EINVAL; - } - - set_balance(sd); - - return 0; -} - -static int mt9v011_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, - enum v4l2_mbus_pixelcode *code) -{ - if (index > 0) - return -EINVAL; - - *code = V4L2_MBUS_FMT_SGRBG8_1X8; - return 0; -} - -static int mt9v011_try_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) -{ - if (fmt->code != V4L2_MBUS_FMT_SGRBG8_1X8) - return -EINVAL; - - v4l_bound_align_image(&fmt->width, 48, 639, 1, - &fmt->height, 32, 480, 1, 0); - fmt->field = V4L2_FIELD_NONE; - fmt->colorspace = V4L2_COLORSPACE_SRGB; - - return 0; -} - -static int mt9v011_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) -{ - struct v4l2_captureparm *cp = &parms->parm.capture; - - if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - memset(cp, 0, sizeof(struct v4l2_captureparm)); - cp->capability = V4L2_CAP_TIMEPERFRAME; - calc_fps(sd, - &cp->timeperframe.numerator, - &cp->timeperframe.denominator); - - return 0; -} - -static int mt9v011_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) -{ - struct v4l2_captureparm *cp = &parms->parm.capture; - struct v4l2_fract *tpf = &cp->timeperframe; - u16 speed; - - if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (cp->extendedmode != 0) - return -EINVAL; - - speed = calc_speed(sd, tpf->numerator, tpf->denominator); - - mt9v011_write(sd, R0A_MT9V011_CLK_SPEED, speed); - v4l2_dbg(1, debug, sd, "Setting speed to %d\n", speed); - - /* Recalculate and update fps info */ - calc_fps(sd, &tpf->numerator, &tpf->denominator); - - return 0; -} - -static int mt9v011_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) -{ - struct mt9v011 *core = to_mt9v011(sd); - int rc; - - rc = mt9v011_try_mbus_fmt(sd, fmt); - if (rc < 0) - return -EINVAL; - - core->width = fmt->width; - core->height = fmt->height; - - set_res(sd); - - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int mt9v011_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - reg->val = mt9v011_read(sd, reg->reg & 0xff); - reg->size = 2; - - return 0; -} - -static int mt9v011_s_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - mt9v011_write(sd, reg->reg & 0xff, reg->val & 0xffff); - - return 0; -} -#endif - -static int mt9v011_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *chip) -{ - u16 version; - struct i2c_client *client = v4l2_get_subdevdata(sd); - - version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_MT9V011, - version); -} - -static const struct v4l2_subdev_core_ops mt9v011_core_ops = { - .queryctrl = mt9v011_queryctrl, - .g_ctrl = mt9v011_g_ctrl, - .s_ctrl = mt9v011_s_ctrl, - .reset = mt9v011_reset, - .g_chip_ident = mt9v011_g_chip_ident, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = mt9v011_g_register, - .s_register = mt9v011_s_register, -#endif -}; - -static const struct v4l2_subdev_video_ops mt9v011_video_ops = { - .enum_mbus_fmt = mt9v011_enum_mbus_fmt, - .try_mbus_fmt = mt9v011_try_mbus_fmt, - .s_mbus_fmt = mt9v011_s_mbus_fmt, - .g_parm = mt9v011_g_parm, - .s_parm = mt9v011_s_parm, -}; - -static const struct v4l2_subdev_ops mt9v011_ops = { - .core = &mt9v011_core_ops, - .video = &mt9v011_video_ops, -}; - - -/**************************************************************************** - I2C Client & Driver - ****************************************************************************/ - -static int mt9v011_probe(struct i2c_client *c, - const struct i2c_device_id *id) -{ - u16 version; - struct mt9v011 *core; - struct v4l2_subdev *sd; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(c->adapter, - I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) - return -EIO; - - core = kzalloc(sizeof(struct mt9v011), GFP_KERNEL); - if (!core) - return -ENOMEM; - - sd = &core->sd; - v4l2_i2c_subdev_init(sd, c, &mt9v011_ops); - - /* Check if the sensor is really a MT9V011 */ - version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION); - if ((version != MT9V011_VERSION) && - (version != MT9V011_REV_B_VERSION)) { - v4l2_info(sd, "*** unknown micron chip detected (0x%04x).\n", - version); - kfree(core); - return -EINVAL; - } - - core->global_gain = 0x0024; - core->exposure = 0x01fc; - core->width = 640; - core->height = 480; - core->xtal = 27000000; /* Hz */ - - if (c->dev.platform_data) { - struct mt9v011_platform_data *pdata = c->dev.platform_data; - - core->xtal = pdata->xtal; - v4l2_dbg(1, debug, sd, "xtal set to %d.%03d MHz\n", - core->xtal / 1000000, (core->xtal / 1000) % 1000); - } - - v4l_info(c, "chip found @ 0x%02x (%s - chip version 0x%04x)\n", - c->addr << 1, c->adapter->name, version); - - return 0; -} - -static int mt9v011_remove(struct i2c_client *c) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(c); - - v4l2_dbg(1, debug, sd, - "mt9v011.c: removing mt9v011 adapter on address 0x%x\n", - c->addr << 1); - - v4l2_device_unregister_subdev(sd); - kfree(to_mt9v011(sd)); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id mt9v011_id[] = { - { "mt9v011", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, mt9v011_id); - -static struct i2c_driver mt9v011_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "mt9v011", - }, - .probe = mt9v011_probe, - .remove = mt9v011_remove, - .id_table = mt9v011_id, -}; - -module_i2c_driver(mt9v011_driver); diff --git a/drivers/media/video/mt9v032.c b/drivers/media/video/mt9v032.c deleted file mode 100644 index 4ba4884c016..00000000000 --- a/drivers/media/video/mt9v032.c +++ /dev/null @@ -1,763 +0,0 @@ -/* - * Driver for MT9V032 CMOS Image Sensor from Micron - * - * Copyright (C) 2010, Laurent Pinchart - * - * Based on the MT9M001 driver, - * - * Copyright (C) 2008, Guennadi Liakhovetski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define MT9V032_PIXEL_ARRAY_HEIGHT 492 -#define MT9V032_PIXEL_ARRAY_WIDTH 782 - -#define MT9V032_CHIP_VERSION 0x00 -#define MT9V032_CHIP_ID_REV1 0x1311 -#define MT9V032_CHIP_ID_REV3 0x1313 -#define MT9V032_COLUMN_START 0x01 -#define MT9V032_COLUMN_START_MIN 1 -#define MT9V032_COLUMN_START_DEF 1 -#define MT9V032_COLUMN_START_MAX 752 -#define MT9V032_ROW_START 0x02 -#define MT9V032_ROW_START_MIN 4 -#define MT9V032_ROW_START_DEF 5 -#define MT9V032_ROW_START_MAX 482 -#define MT9V032_WINDOW_HEIGHT 0x03 -#define MT9V032_WINDOW_HEIGHT_MIN 1 -#define MT9V032_WINDOW_HEIGHT_DEF 480 -#define MT9V032_WINDOW_HEIGHT_MAX 480 -#define MT9V032_WINDOW_WIDTH 0x04 -#define MT9V032_WINDOW_WIDTH_MIN 1 -#define MT9V032_WINDOW_WIDTH_DEF 752 -#define MT9V032_WINDOW_WIDTH_MAX 752 -#define MT9V032_HORIZONTAL_BLANKING 0x05 -#define MT9V032_HORIZONTAL_BLANKING_MIN 43 -#define MT9V032_HORIZONTAL_BLANKING_MAX 1023 -#define MT9V032_VERTICAL_BLANKING 0x06 -#define MT9V032_VERTICAL_BLANKING_MIN 4 -#define MT9V032_VERTICAL_BLANKING_MAX 3000 -#define MT9V032_CHIP_CONTROL 0x07 -#define MT9V032_CHIP_CONTROL_MASTER_MODE (1 << 3) -#define MT9V032_CHIP_CONTROL_DOUT_ENABLE (1 << 7) -#define MT9V032_CHIP_CONTROL_SEQUENTIAL (1 << 8) -#define MT9V032_SHUTTER_WIDTH1 0x08 -#define MT9V032_SHUTTER_WIDTH2 0x09 -#define MT9V032_SHUTTER_WIDTH_CONTROL 0x0a -#define MT9V032_TOTAL_SHUTTER_WIDTH 0x0b -#define MT9V032_TOTAL_SHUTTER_WIDTH_MIN 1 -#define MT9V032_TOTAL_SHUTTER_WIDTH_DEF 480 -#define MT9V032_TOTAL_SHUTTER_WIDTH_MAX 32767 -#define MT9V032_RESET 0x0c -#define MT9V032_READ_MODE 0x0d -#define MT9V032_READ_MODE_ROW_BIN_MASK (3 << 0) -#define MT9V032_READ_MODE_ROW_BIN_SHIFT 0 -#define MT9V032_READ_MODE_COLUMN_BIN_MASK (3 << 2) -#define MT9V032_READ_MODE_COLUMN_BIN_SHIFT 2 -#define MT9V032_READ_MODE_ROW_FLIP (1 << 4) -#define MT9V032_READ_MODE_COLUMN_FLIP (1 << 5) -#define MT9V032_READ_MODE_DARK_COLUMNS (1 << 6) -#define MT9V032_READ_MODE_DARK_ROWS (1 << 7) -#define MT9V032_PIXEL_OPERATION_MODE 0x0f -#define MT9V032_PIXEL_OPERATION_MODE_COLOR (1 << 2) -#define MT9V032_PIXEL_OPERATION_MODE_HDR (1 << 6) -#define MT9V032_ANALOG_GAIN 0x35 -#define MT9V032_ANALOG_GAIN_MIN 16 -#define MT9V032_ANALOG_GAIN_DEF 16 -#define MT9V032_ANALOG_GAIN_MAX 64 -#define MT9V032_MAX_ANALOG_GAIN 0x36 -#define MT9V032_MAX_ANALOG_GAIN_MAX 127 -#define MT9V032_FRAME_DARK_AVERAGE 0x42 -#define MT9V032_DARK_AVG_THRESH 0x46 -#define MT9V032_DARK_AVG_LOW_THRESH_MASK (255 << 0) -#define MT9V032_DARK_AVG_LOW_THRESH_SHIFT 0 -#define MT9V032_DARK_AVG_HIGH_THRESH_MASK (255 << 8) -#define MT9V032_DARK_AVG_HIGH_THRESH_SHIFT 8 -#define MT9V032_ROW_NOISE_CORR_CONTROL 0x70 -#define MT9V032_ROW_NOISE_CORR_ENABLE (1 << 5) -#define MT9V032_ROW_NOISE_CORR_USE_BLK_AVG (1 << 7) -#define MT9V032_PIXEL_CLOCK 0x74 -#define MT9V032_PIXEL_CLOCK_INV_LINE (1 << 0) -#define MT9V032_PIXEL_CLOCK_INV_FRAME (1 << 1) -#define MT9V032_PIXEL_CLOCK_XOR_LINE (1 << 2) -#define MT9V032_PIXEL_CLOCK_CONT_LINE (1 << 3) -#define MT9V032_PIXEL_CLOCK_INV_PXL_CLK (1 << 4) -#define MT9V032_TEST_PATTERN 0x7f -#define MT9V032_TEST_PATTERN_DATA_MASK (1023 << 0) -#define MT9V032_TEST_PATTERN_DATA_SHIFT 0 -#define MT9V032_TEST_PATTERN_USE_DATA (1 << 10) -#define MT9V032_TEST_PATTERN_GRAY_MASK (3 << 11) -#define MT9V032_TEST_PATTERN_GRAY_NONE (0 << 11) -#define MT9V032_TEST_PATTERN_GRAY_VERTICAL (1 << 11) -#define MT9V032_TEST_PATTERN_GRAY_HORIZONTAL (2 << 11) -#define MT9V032_TEST_PATTERN_GRAY_DIAGONAL (3 << 11) -#define MT9V032_TEST_PATTERN_ENABLE (1 << 13) -#define MT9V032_TEST_PATTERN_FLIP (1 << 14) -#define MT9V032_AEC_AGC_ENABLE 0xaf -#define MT9V032_AEC_ENABLE (1 << 0) -#define MT9V032_AGC_ENABLE (1 << 1) -#define MT9V032_THERMAL_INFO 0xc1 - -struct mt9v032 { - struct v4l2_subdev subdev; - struct media_pad pad; - - struct v4l2_mbus_framefmt format; - struct v4l2_rect crop; - - struct v4l2_ctrl_handler ctrls; - - struct mutex power_lock; - int power_count; - - struct mt9v032_platform_data *pdata; - u16 chip_control; - u16 aec_agc; -}; - -static struct mt9v032 *to_mt9v032(struct v4l2_subdev *sd) -{ - return container_of(sd, struct mt9v032, subdev); -} - -static int mt9v032_read(struct i2c_client *client, const u8 reg) -{ - s32 data = i2c_smbus_read_word_swapped(client, reg); - dev_dbg(&client->dev, "%s: read 0x%04x from 0x%02x\n", __func__, - data, reg); - return data; -} - -static int mt9v032_write(struct i2c_client *client, const u8 reg, - const u16 data) -{ - dev_dbg(&client->dev, "%s: writing 0x%04x to 0x%02x\n", __func__, - data, reg); - return i2c_smbus_write_word_swapped(client, reg, data); -} - -static int mt9v032_set_chip_control(struct mt9v032 *mt9v032, u16 clear, u16 set) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); - u16 value = (mt9v032->chip_control & ~clear) | set; - int ret; - - ret = mt9v032_write(client, MT9V032_CHIP_CONTROL, value); - if (ret < 0) - return ret; - - mt9v032->chip_control = value; - return 0; -} - -static int -mt9v032_update_aec_agc(struct mt9v032 *mt9v032, u16 which, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); - u16 value = mt9v032->aec_agc; - int ret; - - if (enable) - value |= which; - else - value &= ~which; - - ret = mt9v032_write(client, MT9V032_AEC_AGC_ENABLE, value); - if (ret < 0) - return ret; - - mt9v032->aec_agc = value; - return 0; -} - -static int mt9v032_power_on(struct mt9v032 *mt9v032) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); - int ret; - - if (mt9v032->pdata->set_clock) { - mt9v032->pdata->set_clock(&mt9v032->subdev, 25000000); - udelay(1); - } - - /* Reset the chip and stop data read out */ - ret = mt9v032_write(client, MT9V032_RESET, 1); - if (ret < 0) - return ret; - - ret = mt9v032_write(client, MT9V032_RESET, 0); - if (ret < 0) - return ret; - - return mt9v032_write(client, MT9V032_CHIP_CONTROL, 0); -} - -static void mt9v032_power_off(struct mt9v032 *mt9v032) -{ - if (mt9v032->pdata->set_clock) - mt9v032->pdata->set_clock(&mt9v032->subdev, 0); -} - -static int __mt9v032_set_power(struct mt9v032 *mt9v032, bool on) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); - int ret; - - if (!on) { - mt9v032_power_off(mt9v032); - return 0; - } - - ret = mt9v032_power_on(mt9v032); - if (ret < 0) - return ret; - - /* Configure the pixel clock polarity */ - if (mt9v032->pdata && mt9v032->pdata->clk_pol) { - ret = mt9v032_write(client, MT9V032_PIXEL_CLOCK, - MT9V032_PIXEL_CLOCK_INV_PXL_CLK); - if (ret < 0) - return ret; - } - - /* Disable the noise correction algorithm and restore the controls. */ - ret = mt9v032_write(client, MT9V032_ROW_NOISE_CORR_CONTROL, 0); - if (ret < 0) - return ret; - - return v4l2_ctrl_handler_setup(&mt9v032->ctrls); -} - -/* ----------------------------------------------------------------------------- - * V4L2 subdev video operations - */ - -static struct v4l2_mbus_framefmt * -__mt9v032_get_pad_format(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh, - unsigned int pad, enum v4l2_subdev_format_whence which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(fh, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &mt9v032->format; - default: - return NULL; - } -} - -static struct v4l2_rect * -__mt9v032_get_pad_crop(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh, - unsigned int pad, enum v4l2_subdev_format_whence which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(fh, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &mt9v032->crop; - default: - return NULL; - } -} - -static int mt9v032_s_stream(struct v4l2_subdev *subdev, int enable) -{ - const u16 mode = MT9V032_CHIP_CONTROL_MASTER_MODE - | MT9V032_CHIP_CONTROL_DOUT_ENABLE - | MT9V032_CHIP_CONTROL_SEQUENTIAL; - struct i2c_client *client = v4l2_get_subdevdata(subdev); - struct mt9v032 *mt9v032 = to_mt9v032(subdev); - struct v4l2_mbus_framefmt *format = &mt9v032->format; - struct v4l2_rect *crop = &mt9v032->crop; - unsigned int hratio; - unsigned int vratio; - int ret; - - if (!enable) - return mt9v032_set_chip_control(mt9v032, mode, 0); - - /* Configure the window size and row/column bin */ - hratio = DIV_ROUND_CLOSEST(crop->width, format->width); - vratio = DIV_ROUND_CLOSEST(crop->height, format->height); - - ret = mt9v032_write(client, MT9V032_READ_MODE, - (hratio - 1) << MT9V032_READ_MODE_ROW_BIN_SHIFT | - (vratio - 1) << MT9V032_READ_MODE_COLUMN_BIN_SHIFT); - if (ret < 0) - return ret; - - ret = mt9v032_write(client, MT9V032_COLUMN_START, crop->left); - if (ret < 0) - return ret; - - ret = mt9v032_write(client, MT9V032_ROW_START, crop->top); - if (ret < 0) - return ret; - - ret = mt9v032_write(client, MT9V032_WINDOW_WIDTH, crop->width); - if (ret < 0) - return ret; - - ret = mt9v032_write(client, MT9V032_WINDOW_HEIGHT, crop->height); - if (ret < 0) - return ret; - - ret = mt9v032_write(client, MT9V032_HORIZONTAL_BLANKING, - max(43, 660 - crop->width)); - if (ret < 0) - return ret; - - /* Switch to master "normal" mode */ - return mt9v032_set_chip_control(mt9v032, 0, mode); -} - -static int mt9v032_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->index > 0) - return -EINVAL; - - code->code = V4L2_MBUS_FMT_SGRBG10_1X10; - return 0; -} - -static int mt9v032_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_frame_size_enum *fse) -{ - if (fse->index >= 8 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10) - return -EINVAL; - - fse->min_width = MT9V032_WINDOW_WIDTH_DEF / fse->index; - fse->max_width = fse->min_width; - fse->min_height = MT9V032_WINDOW_HEIGHT_DEF / fse->index; - fse->max_height = fse->min_height; - - return 0; -} - -static int mt9v032_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *format) -{ - struct mt9v032 *mt9v032 = to_mt9v032(subdev); - - format->format = *__mt9v032_get_pad_format(mt9v032, fh, format->pad, - format->which); - return 0; -} - -static int mt9v032_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *format) -{ - struct mt9v032 *mt9v032 = to_mt9v032(subdev); - struct v4l2_mbus_framefmt *__format; - struct v4l2_rect *__crop; - unsigned int width; - unsigned int height; - unsigned int hratio; - unsigned int vratio; - - __crop = __mt9v032_get_pad_crop(mt9v032, fh, format->pad, - format->which); - - /* Clamp the width and height to avoid dividing by zero. */ - width = clamp_t(unsigned int, ALIGN(format->format.width, 2), - max(__crop->width / 8, MT9V032_WINDOW_WIDTH_MIN), - __crop->width); - height = clamp_t(unsigned int, ALIGN(format->format.height, 2), - max(__crop->height / 8, MT9V032_WINDOW_HEIGHT_MIN), - __crop->height); - - hratio = DIV_ROUND_CLOSEST(__crop->width, width); - vratio = DIV_ROUND_CLOSEST(__crop->height, height); - - __format = __mt9v032_get_pad_format(mt9v032, fh, format->pad, - format->which); - __format->width = __crop->width / hratio; - __format->height = __crop->height / vratio; - - format->format = *__format; - - return 0; -} - -static int mt9v032_get_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) -{ - struct mt9v032 *mt9v032 = to_mt9v032(subdev); - - crop->rect = *__mt9v032_get_pad_crop(mt9v032, fh, crop->pad, - crop->which); - return 0; -} - -static int mt9v032_set_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) -{ - struct mt9v032 *mt9v032 = to_mt9v032(subdev); - struct v4l2_mbus_framefmt *__format; - struct v4l2_rect *__crop; - struct v4l2_rect rect; - - /* Clamp the crop rectangle boundaries and align them to a non multiple - * of 2 pixels to ensure a GRBG Bayer pattern. - */ - rect.left = clamp(ALIGN(crop->rect.left + 1, 2) - 1, - MT9V032_COLUMN_START_MIN, - MT9V032_COLUMN_START_MAX); - rect.top = clamp(ALIGN(crop->rect.top + 1, 2) - 1, - MT9V032_ROW_START_MIN, - MT9V032_ROW_START_MAX); - rect.width = clamp(ALIGN(crop->rect.width, 2), - MT9V032_WINDOW_WIDTH_MIN, - MT9V032_WINDOW_WIDTH_MAX); - rect.height = clamp(ALIGN(crop->rect.height, 2), - MT9V032_WINDOW_HEIGHT_MIN, - MT9V032_WINDOW_HEIGHT_MAX); - - rect.width = min(rect.width, MT9V032_PIXEL_ARRAY_WIDTH - rect.left); - rect.height = min(rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top); - - __crop = __mt9v032_get_pad_crop(mt9v032, fh, crop->pad, crop->which); - - if (rect.width != __crop->width || rect.height != __crop->height) { - /* Reset the output image size if the crop rectangle size has - * been modified. - */ - __format = __mt9v032_get_pad_format(mt9v032, fh, crop->pad, - crop->which); - __format->width = rect.width; - __format->height = rect.height; - } - - *__crop = rect; - crop->rect = rect; - - return 0; -} - -/* ----------------------------------------------------------------------------- - * V4L2 subdev control operations - */ - -#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001) - -static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9v032 *mt9v032 = - container_of(ctrl->handler, struct mt9v032, ctrls); - struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); - u16 data; - - switch (ctrl->id) { - case V4L2_CID_AUTOGAIN: - return mt9v032_update_aec_agc(mt9v032, MT9V032_AGC_ENABLE, - ctrl->val); - - case V4L2_CID_GAIN: - return mt9v032_write(client, MT9V032_ANALOG_GAIN, ctrl->val); - - case V4L2_CID_EXPOSURE_AUTO: - return mt9v032_update_aec_agc(mt9v032, MT9V032_AEC_ENABLE, - !ctrl->val); - - case V4L2_CID_EXPOSURE: - return mt9v032_write(client, MT9V032_TOTAL_SHUTTER_WIDTH, - ctrl->val); - - case V4L2_CID_TEST_PATTERN: - switch (ctrl->val) { - case 0: - data = 0; - break; - case 1: - data = MT9V032_TEST_PATTERN_GRAY_VERTICAL - | MT9V032_TEST_PATTERN_ENABLE; - break; - case 2: - data = MT9V032_TEST_PATTERN_GRAY_HORIZONTAL - | MT9V032_TEST_PATTERN_ENABLE; - break; - case 3: - data = MT9V032_TEST_PATTERN_GRAY_DIAGONAL - | MT9V032_TEST_PATTERN_ENABLE; - break; - default: - data = (ctrl->val << MT9V032_TEST_PATTERN_DATA_SHIFT) - | MT9V032_TEST_PATTERN_USE_DATA - | MT9V032_TEST_PATTERN_ENABLE - | MT9V032_TEST_PATTERN_FLIP; - break; - } - - return mt9v032_write(client, MT9V032_TEST_PATTERN, data); - } - - return 0; -} - -static struct v4l2_ctrl_ops mt9v032_ctrl_ops = { - .s_ctrl = mt9v032_s_ctrl, -}; - -static const struct v4l2_ctrl_config mt9v032_ctrls[] = { - { - .ops = &mt9v032_ctrl_ops, - .id = V4L2_CID_TEST_PATTERN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Test pattern", - .min = 0, - .max = 1023, - .step = 1, - .def = 0, - .flags = 0, - } -}; - -/* ----------------------------------------------------------------------------- - * V4L2 subdev core operations - */ - -static int mt9v032_set_power(struct v4l2_subdev *subdev, int on) -{ - struct mt9v032 *mt9v032 = to_mt9v032(subdev); - int ret = 0; - - mutex_lock(&mt9v032->power_lock); - - /* If the power count is modified from 0 to != 0 or from != 0 to 0, - * update the power state. - */ - if (mt9v032->power_count == !on) { - ret = __mt9v032_set_power(mt9v032, !!on); - if (ret < 0) - goto done; - } - - /* Update the power count. */ - mt9v032->power_count += on ? 1 : -1; - WARN_ON(mt9v032->power_count < 0); - -done: - mutex_unlock(&mt9v032->power_lock); - return ret; -} - -/* ----------------------------------------------------------------------------- - * V4L2 subdev internal operations - */ - -static int mt9v032_registered(struct v4l2_subdev *subdev) -{ - struct i2c_client *client = v4l2_get_subdevdata(subdev); - struct mt9v032 *mt9v032 = to_mt9v032(subdev); - s32 data; - int ret; - - dev_info(&client->dev, "Probing MT9V032 at address 0x%02x\n", - client->addr); - - ret = mt9v032_power_on(mt9v032); - if (ret < 0) { - dev_err(&client->dev, "MT9V032 power up failed\n"); - return ret; - } - - /* Read and check the sensor version */ - data = mt9v032_read(client, MT9V032_CHIP_VERSION); - if (data != MT9V032_CHIP_ID_REV1 && data != MT9V032_CHIP_ID_REV3) { - dev_err(&client->dev, "MT9V032 not detected, wrong version " - "0x%04x\n", data); - return -ENODEV; - } - - mt9v032_power_off(mt9v032); - - dev_info(&client->dev, "MT9V032 detected at address 0x%02x\n", - client->addr); - - return ret; -} - -static int mt9v032_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) -{ - struct v4l2_mbus_framefmt *format; - struct v4l2_rect *crop; - - crop = v4l2_subdev_get_try_crop(fh, 0); - crop->left = MT9V032_COLUMN_START_DEF; - crop->top = MT9V032_ROW_START_DEF; - crop->width = MT9V032_WINDOW_WIDTH_DEF; - crop->height = MT9V032_WINDOW_HEIGHT_DEF; - - format = v4l2_subdev_get_try_format(fh, 0); - format->code = V4L2_MBUS_FMT_SGRBG10_1X10; - format->width = MT9V032_WINDOW_WIDTH_DEF; - format->height = MT9V032_WINDOW_HEIGHT_DEF; - format->field = V4L2_FIELD_NONE; - format->colorspace = V4L2_COLORSPACE_SRGB; - - return mt9v032_set_power(subdev, 1); -} - -static int mt9v032_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) -{ - return mt9v032_set_power(subdev, 0); -} - -static struct v4l2_subdev_core_ops mt9v032_subdev_core_ops = { - .s_power = mt9v032_set_power, -}; - -static struct v4l2_subdev_video_ops mt9v032_subdev_video_ops = { - .s_stream = mt9v032_s_stream, -}; - -static struct v4l2_subdev_pad_ops mt9v032_subdev_pad_ops = { - .enum_mbus_code = mt9v032_enum_mbus_code, - .enum_frame_size = mt9v032_enum_frame_size, - .get_fmt = mt9v032_get_format, - .set_fmt = mt9v032_set_format, - .get_crop = mt9v032_get_crop, - .set_crop = mt9v032_set_crop, -}; - -static struct v4l2_subdev_ops mt9v032_subdev_ops = { - .core = &mt9v032_subdev_core_ops, - .video = &mt9v032_subdev_video_ops, - .pad = &mt9v032_subdev_pad_ops, -}; - -static const struct v4l2_subdev_internal_ops mt9v032_subdev_internal_ops = { - .registered = mt9v032_registered, - .open = mt9v032_open, - .close = mt9v032_close, -}; - -/* ----------------------------------------------------------------------------- - * Driver initialization and probing - */ - -static int mt9v032_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct mt9v032 *mt9v032; - unsigned int i; - int ret; - - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_WORD_DATA)) { - dev_warn(&client->adapter->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); - return -EIO; - } - - mt9v032 = kzalloc(sizeof(*mt9v032), GFP_KERNEL); - if (!mt9v032) - return -ENOMEM; - - mutex_init(&mt9v032->power_lock); - mt9v032->pdata = client->dev.platform_data; - - v4l2_ctrl_handler_init(&mt9v032->ctrls, ARRAY_SIZE(mt9v032_ctrls) + 4); - - v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, - V4L2_CID_GAIN, MT9V032_ANALOG_GAIN_MIN, - MT9V032_ANALOG_GAIN_MAX, 1, MT9V032_ANALOG_GAIN_DEF); - v4l2_ctrl_new_std_menu(&mt9v032->ctrls, &mt9v032_ctrl_ops, - V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0, - V4L2_EXPOSURE_AUTO); - v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, - V4L2_CID_EXPOSURE, MT9V032_TOTAL_SHUTTER_WIDTH_MIN, - MT9V032_TOTAL_SHUTTER_WIDTH_MAX, 1, - MT9V032_TOTAL_SHUTTER_WIDTH_DEF); - - for (i = 0; i < ARRAY_SIZE(mt9v032_ctrls); ++i) - v4l2_ctrl_new_custom(&mt9v032->ctrls, &mt9v032_ctrls[i], NULL); - - mt9v032->subdev.ctrl_handler = &mt9v032->ctrls; - - if (mt9v032->ctrls.error) - printk(KERN_INFO "%s: control initialization error %d\n", - __func__, mt9v032->ctrls.error); - - mt9v032->crop.left = MT9V032_COLUMN_START_DEF; - mt9v032->crop.top = MT9V032_ROW_START_DEF; - mt9v032->crop.width = MT9V032_WINDOW_WIDTH_DEF; - mt9v032->crop.height = MT9V032_WINDOW_HEIGHT_DEF; - - mt9v032->format.code = V4L2_MBUS_FMT_SGRBG10_1X10; - mt9v032->format.width = MT9V032_WINDOW_WIDTH_DEF; - mt9v032->format.height = MT9V032_WINDOW_HEIGHT_DEF; - mt9v032->format.field = V4L2_FIELD_NONE; - mt9v032->format.colorspace = V4L2_COLORSPACE_SRGB; - - mt9v032->aec_agc = MT9V032_AEC_ENABLE | MT9V032_AGC_ENABLE; - - v4l2_i2c_subdev_init(&mt9v032->subdev, client, &mt9v032_subdev_ops); - mt9v032->subdev.internal_ops = &mt9v032_subdev_internal_ops; - mt9v032->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - mt9v032->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&mt9v032->subdev.entity, 1, &mt9v032->pad, 0); - if (ret < 0) - kfree(mt9v032); - - return ret; -} - -static int mt9v032_remove(struct i2c_client *client) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct mt9v032 *mt9v032 = to_mt9v032(subdev); - - v4l2_device_unregister_subdev(subdev); - media_entity_cleanup(&subdev->entity); - kfree(mt9v032); - return 0; -} - -static const struct i2c_device_id mt9v032_id[] = { - { "mt9v032", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, mt9v032_id); - -static struct i2c_driver mt9v032_driver = { - .driver = { - .name = "mt9v032", - }, - .probe = mt9v032_probe, - .remove = mt9v032_remove, - .id_table = mt9v032_id, -}; - -module_i2c_driver(mt9v032_driver); - -MODULE_DESCRIPTION("Aptina MT9V032 Camera driver"); -MODULE_AUTHOR("Laurent Pinchart "); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/noon010pc30.c b/drivers/media/video/noon010pc30.c deleted file mode 100644 index 440c12962ba..00000000000 --- a/drivers/media/video/noon010pc30.c +++ /dev/null @@ -1,851 +0,0 @@ -/* - * Driver for SiliconFile NOON010PC30 CIF (1/11") Image Sensor with ISP - * - * Copyright (C) 2010 - 2011 Samsung Electronics Co., Ltd. - * Contact: Sylwester Nawrocki, - * - * Initial register configuration based on a driver authored by - * HeungJun Kim . - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Enable module debug trace. Set to 1 to enable."); - -#define MODULE_NAME "NOON010PC30" - -/* - * Register offsets within a page - * b15..b8 - page id, b7..b0 - register address - */ -#define POWER_CTRL_REG 0x0001 -#define PAGEMODE_REG 0x03 -#define DEVICE_ID_REG 0x0004 -#define NOON010PC30_ID 0x86 -#define VDO_CTL_REG(n) (0x0010 + (n)) -#define SYNC_CTL_REG 0x0012 -/* Window size and position */ -#define WIN_ROWH_REG 0x0013 -#define WIN_ROWL_REG 0x0014 -#define WIN_COLH_REG 0x0015 -#define WIN_COLL_REG 0x0016 -#define WIN_HEIGHTH_REG 0x0017 -#define WIN_HEIGHTL_REG 0x0018 -#define WIN_WIDTHH_REG 0x0019 -#define WIN_WIDTHL_REG 0x001A -#define HBLANKH_REG 0x001B -#define HBLANKL_REG 0x001C -#define VSYNCH_REG 0x001D -#define VSYNCL_REG 0x001E -/* VSYNC control */ -#define VS_CTL_REG(n) (0x00A1 + (n)) -/* page 1 */ -#define ISP_CTL_REG(n) (0x0110 + (n)) -#define YOFS_REG 0x0119 -#define DARK_YOFS_REG 0x011A -#define SAT_CTL_REG 0x0120 -#define BSAT_REG 0x0121 -#define RSAT_REG 0x0122 -/* Color correction */ -#define CMC_CTL_REG 0x0130 -#define CMC_OFSGH_REG 0x0133 -#define CMC_OFSGL_REG 0x0135 -#define CMC_SIGN_REG 0x0136 -#define CMC_GOFS_REG 0x0137 -#define CMC_COEF_REG(n) (0x0138 + (n)) -#define CMC_OFS_REG(n) (0x0141 + (n)) -/* Gamma correction */ -#define GMA_CTL_REG 0x0160 -#define GMA_COEF_REG(n) (0x0161 + (n)) -/* Lens Shading */ -#define LENS_CTRL_REG 0x01D0 -#define LENS_XCEN_REG 0x01D1 -#define LENS_YCEN_REG 0x01D2 -#define LENS_RC_REG 0x01D3 -#define LENS_GC_REG 0x01D4 -#define LENS_BC_REG 0x01D5 -#define L_AGON_REG 0x01D6 -#define L_AGOFF_REG 0x01D7 -/* Page 3 - Auto Exposure */ -#define AE_CTL_REG(n) (0x0310 + (n)) -#define AE_CTL9_REG 0x032C -#define AE_CTL10_REG 0x032D -#define AE_YLVL_REG 0x031C -#define AE_YTH_REG(n) (0x031D + (n)) -#define AE_WGT_REG 0x0326 -#define EXP_TIMEH_REG 0x0333 -#define EXP_TIMEM_REG 0x0334 -#define EXP_TIMEL_REG 0x0335 -#define EXP_MMINH_REG 0x0336 -#define EXP_MMINL_REG 0x0337 -#define EXP_MMAXH_REG 0x0338 -#define EXP_MMAXM_REG 0x0339 -#define EXP_MMAXL_REG 0x033A -/* Page 4 - Auto White Balance */ -#define AWB_CTL_REG(n) (0x0410 + (n)) -#define AWB_ENABE 0x80 -#define AWB_WGHT_REG 0x0419 -#define BGAIN_PAR_REG(n) (0x044F + (n)) -/* Manual white balance, when AWB_CTL2[0]=1 */ -#define MWB_RGAIN_REG 0x0466 -#define MWB_BGAIN_REG 0x0467 - -/* The token to mark an array end */ -#define REG_TERM 0xFFFF - -struct noon010_format { - enum v4l2_mbus_pixelcode code; - enum v4l2_colorspace colorspace; - u16 ispctl1_reg; -}; - -struct noon010_frmsize { - u16 width; - u16 height; - int vid_ctl1; -}; - -static const char * const noon010_supply_name[] = { - "vdd_core", "vddio", "vdda" -}; - -#define NOON010_NUM_SUPPLIES ARRAY_SIZE(noon010_supply_name) - -struct noon010_info { - struct v4l2_subdev sd; - struct media_pad pad; - struct v4l2_ctrl_handler hdl; - struct regulator_bulk_data supply[NOON010_NUM_SUPPLIES]; - u32 gpio_nreset; - u32 gpio_nstby; - - /* Protects the struct members below */ - struct mutex lock; - - const struct noon010_format *curr_fmt; - const struct noon010_frmsize *curr_win; - unsigned int apply_new_cfg:1; - unsigned int streaming:1; - unsigned int hflip:1; - unsigned int vflip:1; - unsigned int power:1; - u8 i2c_reg_page; -}; - -struct i2c_regval { - u16 addr; - u16 val; -}; - -/* Supported resolutions. */ -static const struct noon010_frmsize noon010_sizes[] = { - { - .width = 352, - .height = 288, - .vid_ctl1 = 0, - }, { - .width = 176, - .height = 144, - .vid_ctl1 = 0x10, - }, { - .width = 88, - .height = 72, - .vid_ctl1 = 0x20, - }, -}; - -/* Supported pixel formats. */ -static const struct noon010_format noon010_formats[] = { - { - .code = V4L2_MBUS_FMT_YUYV8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0x03, - }, { - .code = V4L2_MBUS_FMT_YVYU8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0x02, - }, { - .code = V4L2_MBUS_FMT_VYUY8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0, - }, { - .code = V4L2_MBUS_FMT_UYVY8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0x01, - }, { - .code = V4L2_MBUS_FMT_RGB565_2X8_BE, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0x40, - }, -}; - -static const struct i2c_regval noon010_base_regs[] = { - { WIN_COLL_REG, 0x06 }, { HBLANKL_REG, 0x7C }, - /* Color corection and saturation */ - { ISP_CTL_REG(0), 0x30 }, { ISP_CTL_REG(2), 0x30 }, - { YOFS_REG, 0x80 }, { DARK_YOFS_REG, 0x04 }, - { SAT_CTL_REG, 0x1F }, { BSAT_REG, 0x90 }, - { CMC_CTL_REG, 0x0F }, { CMC_OFSGH_REG, 0x3C }, - { CMC_OFSGL_REG, 0x2C }, { CMC_SIGN_REG, 0x3F }, - { CMC_COEF_REG(0), 0x79 }, { CMC_OFS_REG(0), 0x00 }, - { CMC_COEF_REG(1), 0x39 }, { CMC_OFS_REG(1), 0x00 }, - { CMC_COEF_REG(2), 0x00 }, { CMC_OFS_REG(2), 0x00 }, - { CMC_COEF_REG(3), 0x11 }, { CMC_OFS_REG(3), 0x8B }, - { CMC_COEF_REG(4), 0x65 }, { CMC_OFS_REG(4), 0x07 }, - { CMC_COEF_REG(5), 0x14 }, { CMC_OFS_REG(5), 0x04 }, - { CMC_COEF_REG(6), 0x01 }, { CMC_OFS_REG(6), 0x9C }, - { CMC_COEF_REG(7), 0x33 }, { CMC_OFS_REG(7), 0x89 }, - { CMC_COEF_REG(8), 0x74 }, { CMC_OFS_REG(8), 0x25 }, - /* Automatic white balance */ - { AWB_CTL_REG(0), 0x78 }, { AWB_CTL_REG(1), 0x2E }, - { AWB_CTL_REG(2), 0x20 }, { AWB_CTL_REG(3), 0x85 }, - /* Auto exposure */ - { AE_CTL_REG(0), 0xDC }, { AE_CTL_REG(1), 0x81 }, - { AE_CTL_REG(2), 0x30 }, { AE_CTL_REG(3), 0xA5 }, - { AE_CTL_REG(4), 0x40 }, { AE_CTL_REG(5), 0x51 }, - { AE_CTL_REG(6), 0x33 }, { AE_CTL_REG(7), 0x7E }, - { AE_CTL9_REG, 0x00 }, { AE_CTL10_REG, 0x02 }, - { AE_YLVL_REG, 0x44 }, { AE_YTH_REG(0), 0x34 }, - { AE_YTH_REG(1), 0x30 }, { AE_WGT_REG, 0xD5 }, - /* Lens shading compensation */ - { LENS_CTRL_REG, 0x01 }, { LENS_XCEN_REG, 0x80 }, - { LENS_YCEN_REG, 0x70 }, { LENS_RC_REG, 0x53 }, - { LENS_GC_REG, 0x40 }, { LENS_BC_REG, 0x3E }, - { REG_TERM, 0 }, -}; - -static inline struct noon010_info *to_noon010(struct v4l2_subdev *sd) -{ - return container_of(sd, struct noon010_info, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct noon010_info, hdl)->sd; -} - -static inline int set_i2c_page(struct noon010_info *info, - struct i2c_client *client, unsigned int reg) -{ - u32 page = reg >> 8 & 0xFF; - int ret = 0; - - if (info->i2c_reg_page != page && (reg & 0xFF) != 0x03) { - ret = i2c_smbus_write_byte_data(client, PAGEMODE_REG, page); - if (!ret) - info->i2c_reg_page = page; - } - return ret; -} - -static int cam_i2c_read(struct v4l2_subdev *sd, u32 reg_addr) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct noon010_info *info = to_noon010(sd); - int ret = set_i2c_page(info, client, reg_addr); - - if (ret) - return ret; - return i2c_smbus_read_byte_data(client, reg_addr & 0xFF); -} - -static int cam_i2c_write(struct v4l2_subdev *sd, u32 reg_addr, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct noon010_info *info = to_noon010(sd); - int ret = set_i2c_page(info, client, reg_addr); - - if (ret) - return ret; - return i2c_smbus_write_byte_data(client, reg_addr & 0xFF, val); -} - -static inline int noon010_bulk_write_reg(struct v4l2_subdev *sd, - const struct i2c_regval *msg) -{ - while (msg->addr != REG_TERM) { - int ret = cam_i2c_write(sd, msg->addr, msg->val); - - if (ret) - return ret; - msg++; - } - return 0; -} - -/* Device reset and sleep mode control */ -static int noon010_power_ctrl(struct v4l2_subdev *sd, bool reset, bool sleep) -{ - struct noon010_info *info = to_noon010(sd); - u8 reg = sleep ? 0xF1 : 0xF0; - int ret = 0; - - if (reset) { - ret = cam_i2c_write(sd, POWER_CTRL_REG, reg | 0x02); - udelay(20); - } - if (!ret) { - ret = cam_i2c_write(sd, POWER_CTRL_REG, reg); - if (reset && !ret) - info->i2c_reg_page = -1; - } - return ret; -} - -/* Automatic white balance control */ -static int noon010_enable_autowhitebalance(struct v4l2_subdev *sd, int on) -{ - int ret; - - ret = cam_i2c_write(sd, AWB_CTL_REG(1), on ? 0x2E : 0x2F); - if (!ret) - ret = cam_i2c_write(sd, AWB_CTL_REG(0), on ? 0xFB : 0x7B); - return ret; -} - -/* Called with struct noon010_info.lock mutex held */ -static int noon010_set_flip(struct v4l2_subdev *sd, int hflip, int vflip) -{ - struct noon010_info *info = to_noon010(sd); - int reg, ret; - - reg = cam_i2c_read(sd, VDO_CTL_REG(1)); - if (reg < 0) - return reg; - - reg &= 0x7C; - if (hflip) - reg |= 0x01; - if (vflip) - reg |= 0x02; - - ret = cam_i2c_write(sd, VDO_CTL_REG(1), reg | 0x80); - if (!ret) { - info->hflip = hflip; - info->vflip = vflip; - } - return ret; -} - -/* Configure resolution and color format */ -static int noon010_set_params(struct v4l2_subdev *sd) -{ - struct noon010_info *info = to_noon010(sd); - - int ret = cam_i2c_write(sd, VDO_CTL_REG(0), - info->curr_win->vid_ctl1); - if (ret) - return ret; - return cam_i2c_write(sd, ISP_CTL_REG(0), - info->curr_fmt->ispctl1_reg); -} - -/* Find nearest matching image pixel size. */ -static int noon010_try_frame_size(struct v4l2_mbus_framefmt *mf, - const struct noon010_frmsize **size) -{ - unsigned int min_err = ~0; - int i = ARRAY_SIZE(noon010_sizes); - const struct noon010_frmsize *fsize = &noon010_sizes[0], - *match = NULL; - - while (i--) { - int err = abs(fsize->width - mf->width) - + abs(fsize->height - mf->height); - - if (err < min_err) { - min_err = err; - match = fsize; - } - fsize++; - } - if (match) { - mf->width = match->width; - mf->height = match->height; - if (size) - *size = match; - return 0; - } - return -EINVAL; -} - -/* Called with info.lock mutex held */ -static int power_enable(struct noon010_info *info) -{ - int ret; - - if (info->power) { - v4l2_info(&info->sd, "%s: sensor is already on\n", __func__); - return 0; - } - - if (gpio_is_valid(info->gpio_nstby)) - gpio_set_value(info->gpio_nstby, 0); - - if (gpio_is_valid(info->gpio_nreset)) - gpio_set_value(info->gpio_nreset, 0); - - ret = regulator_bulk_enable(NOON010_NUM_SUPPLIES, info->supply); - if (ret) - return ret; - - if (gpio_is_valid(info->gpio_nreset)) { - msleep(50); - gpio_set_value(info->gpio_nreset, 1); - } - if (gpio_is_valid(info->gpio_nstby)) { - udelay(1000); - gpio_set_value(info->gpio_nstby, 1); - } - if (gpio_is_valid(info->gpio_nreset)) { - udelay(1000); - gpio_set_value(info->gpio_nreset, 0); - msleep(100); - gpio_set_value(info->gpio_nreset, 1); - msleep(20); - } - info->power = 1; - - v4l2_dbg(1, debug, &info->sd, "%s: sensor is on\n", __func__); - return 0; -} - -/* Called with info.lock mutex held */ -static int power_disable(struct noon010_info *info) -{ - int ret; - - if (!info->power) { - v4l2_info(&info->sd, "%s: sensor is already off\n", __func__); - return 0; - } - - ret = regulator_bulk_disable(NOON010_NUM_SUPPLIES, info->supply); - if (ret) - return ret; - - if (gpio_is_valid(info->gpio_nstby)) - gpio_set_value(info->gpio_nstby, 0); - - if (gpio_is_valid(info->gpio_nreset)) - gpio_set_value(info->gpio_nreset, 0); - - info->power = 0; - - v4l2_dbg(1, debug, &info->sd, "%s: sensor is off\n", __func__); - - return 0; -} - -static int noon010_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - struct noon010_info *info = to_noon010(sd); - int ret = 0; - - v4l2_dbg(1, debug, sd, "%s: ctrl_id: %d, value: %d\n", - __func__, ctrl->id, ctrl->val); - - mutex_lock(&info->lock); - /* - * If the device is not powered up by the host driver do - * not apply any controls to H/W at this time. Instead - * the controls will be restored right after power-up. - */ - if (!info->power) - goto unlock; - - switch (ctrl->id) { - case V4L2_CID_AUTO_WHITE_BALANCE: - ret = noon010_enable_autowhitebalance(sd, ctrl->val); - break; - case V4L2_CID_BLUE_BALANCE: - ret = cam_i2c_write(sd, MWB_BGAIN_REG, ctrl->val); - break; - case V4L2_CID_RED_BALANCE: - ret = cam_i2c_write(sd, MWB_RGAIN_REG, ctrl->val); - break; - default: - ret = -EINVAL; - } -unlock: - mutex_unlock(&info->lock); - return ret; -} - -static int noon010_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->index >= ARRAY_SIZE(noon010_formats)) - return -EINVAL; - - code->code = noon010_formats[code->index].code; - return 0; -} - -static int noon010_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *fmt) -{ - struct noon010_info *info = to_noon010(sd); - struct v4l2_mbus_framefmt *mf; - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - if (fh) { - mf = v4l2_subdev_get_try_format(fh, 0); - fmt->format = *mf; - } - return 0; - } - mf = &fmt->format; - - mutex_lock(&info->lock); - mf->width = info->curr_win->width; - mf->height = info->curr_win->height; - mf->code = info->curr_fmt->code; - mf->colorspace = info->curr_fmt->colorspace; - mf->field = V4L2_FIELD_NONE; - - mutex_unlock(&info->lock); - return 0; -} - -/* Return nearest media bus frame format. */ -static const struct noon010_format *noon010_try_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - int i = ARRAY_SIZE(noon010_formats); - - while (--i) - if (mf->code == noon010_formats[i].code) - break; - mf->code = noon010_formats[i].code; - - return &noon010_formats[i]; -} - -static int noon010_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *fmt) -{ - struct noon010_info *info = to_noon010(sd); - const struct noon010_frmsize *size = NULL; - const struct noon010_format *nf; - struct v4l2_mbus_framefmt *mf; - int ret = 0; - - nf = noon010_try_fmt(sd, &fmt->format); - noon010_try_frame_size(&fmt->format, &size); - fmt->format.colorspace = V4L2_COLORSPACE_JPEG; - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - if (fh) { - mf = v4l2_subdev_get_try_format(fh, 0); - *mf = fmt->format; - } - return 0; - } - mutex_lock(&info->lock); - if (!info->streaming) { - info->apply_new_cfg = 1; - info->curr_fmt = nf; - info->curr_win = size; - } else { - ret = -EBUSY; - } - mutex_unlock(&info->lock); - return ret; -} - -/* Called with struct noon010_info.lock mutex held */ -static int noon010_base_config(struct v4l2_subdev *sd) -{ - int ret = noon010_bulk_write_reg(sd, noon010_base_regs); - if (!ret) - ret = noon010_set_params(sd); - if (!ret) - ret = noon010_set_flip(sd, 1, 0); - - return ret; -} - -static int noon010_s_power(struct v4l2_subdev *sd, int on) -{ - struct noon010_info *info = to_noon010(sd); - int ret; - - mutex_lock(&info->lock); - if (on) { - ret = power_enable(info); - if (!ret) - ret = noon010_base_config(sd); - } else { - noon010_power_ctrl(sd, false, true); - ret = power_disable(info); - } - mutex_unlock(&info->lock); - - /* Restore the controls state */ - if (!ret && on) - ret = v4l2_ctrl_handler_setup(&info->hdl); - - return ret; -} - -static int noon010_s_stream(struct v4l2_subdev *sd, int on) -{ - struct noon010_info *info = to_noon010(sd); - int ret = 0; - - mutex_lock(&info->lock); - if (!info->streaming != !on) { - ret = noon010_power_ctrl(sd, false, !on); - if (!ret) - info->streaming = on; - } - if (!ret && on && info->apply_new_cfg) { - ret = noon010_set_params(sd); - if (!ret) - info->apply_new_cfg = 0; - } - mutex_unlock(&info->lock); - return ret; -} - -static int noon010_log_status(struct v4l2_subdev *sd) -{ - struct noon010_info *info = to_noon010(sd); - - v4l2_ctrl_handler_log_status(&info->hdl, sd->name); - return 0; -} - -static int noon010_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(fh, 0); - - mf->width = noon010_sizes[0].width; - mf->height = noon010_sizes[0].height; - mf->code = noon010_formats[0].code; - mf->colorspace = V4L2_COLORSPACE_JPEG; - mf->field = V4L2_FIELD_NONE; - return 0; -} - -static const struct v4l2_subdev_internal_ops noon010_subdev_internal_ops = { - .open = noon010_open, -}; - -static const struct v4l2_ctrl_ops noon010_ctrl_ops = { - .s_ctrl = noon010_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops noon010_core_ops = { - .s_power = noon010_s_power, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .log_status = noon010_log_status, -}; - -static struct v4l2_subdev_pad_ops noon010_pad_ops = { - .enum_mbus_code = noon010_enum_mbus_code, - .get_fmt = noon010_get_fmt, - .set_fmt = noon010_set_fmt, -}; - -static struct v4l2_subdev_video_ops noon010_video_ops = { - .s_stream = noon010_s_stream, -}; - -static const struct v4l2_subdev_ops noon010_ops = { - .core = &noon010_core_ops, - .pad = &noon010_pad_ops, - .video = &noon010_video_ops, -}; - -/* Return 0 if NOON010PC30L sensor type was detected or -ENODEV otherwise. */ -static int noon010_detect(struct i2c_client *client, struct noon010_info *info) -{ - int ret; - - ret = power_enable(info); - if (ret) - return ret; - - ret = i2c_smbus_read_byte_data(client, DEVICE_ID_REG); - if (ret < 0) - dev_err(&client->dev, "I2C read failed: 0x%X\n", ret); - - power_disable(info); - - return ret == NOON010PC30_ID ? 0 : -ENODEV; -} - -static int noon010_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct noon010_info *info; - struct v4l2_subdev *sd; - const struct noon010pc30_platform_data *pdata - = client->dev.platform_data; - int ret; - int i; - - if (!pdata) { - dev_err(&client->dev, "No platform data!\n"); - return -EIO; - } - - info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - mutex_init(&info->lock); - sd = &info->sd; - v4l2_i2c_subdev_init(sd, client, &noon010_ops); - strlcpy(sd->name, MODULE_NAME, sizeof(sd->name)); - - sd->internal_ops = &noon010_subdev_internal_ops; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - v4l2_ctrl_handler_init(&info->hdl, 3); - - v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, - V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); - v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, - V4L2_CID_RED_BALANCE, 0, 127, 1, 64); - v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, - V4L2_CID_BLUE_BALANCE, 0, 127, 1, 64); - - sd->ctrl_handler = &info->hdl; - - ret = info->hdl.error; - if (ret) - goto np_err; - - info->i2c_reg_page = -1; - info->gpio_nreset = -EINVAL; - info->gpio_nstby = -EINVAL; - info->curr_fmt = &noon010_formats[0]; - info->curr_win = &noon010_sizes[0]; - - if (gpio_is_valid(pdata->gpio_nreset)) { - ret = gpio_request(pdata->gpio_nreset, "NOON010PC30 NRST"); - if (ret) { - dev_err(&client->dev, "GPIO request error: %d\n", ret); - goto np_err; - } - info->gpio_nreset = pdata->gpio_nreset; - gpio_direction_output(info->gpio_nreset, 0); - gpio_export(info->gpio_nreset, 0); - } - - if (gpio_is_valid(pdata->gpio_nstby)) { - ret = gpio_request(pdata->gpio_nstby, "NOON010PC30 NSTBY"); - if (ret) { - dev_err(&client->dev, "GPIO request error: %d\n", ret); - goto np_gpio_err; - } - info->gpio_nstby = pdata->gpio_nstby; - gpio_direction_output(info->gpio_nstby, 0); - gpio_export(info->gpio_nstby, 0); - } - - for (i = 0; i < NOON010_NUM_SUPPLIES; i++) - info->supply[i].supply = noon010_supply_name[i]; - - ret = regulator_bulk_get(&client->dev, NOON010_NUM_SUPPLIES, - info->supply); - if (ret) - goto np_reg_err; - - info->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - ret = media_entity_init(&sd->entity, 1, &info->pad, 0); - if (ret < 0) - goto np_me_err; - - ret = noon010_detect(client, info); - if (!ret) - return 0; - -np_me_err: - regulator_bulk_free(NOON010_NUM_SUPPLIES, info->supply); -np_reg_err: - if (gpio_is_valid(info->gpio_nstby)) - gpio_free(info->gpio_nstby); -np_gpio_err: - if (gpio_is_valid(info->gpio_nreset)) - gpio_free(info->gpio_nreset); -np_err: - v4l2_ctrl_handler_free(&info->hdl); - v4l2_device_unregister_subdev(sd); - kfree(info); - return ret; -} - -static int noon010_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct noon010_info *info = to_noon010(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&info->hdl); - - regulator_bulk_free(NOON010_NUM_SUPPLIES, info->supply); - - if (gpio_is_valid(info->gpio_nreset)) - gpio_free(info->gpio_nreset); - - if (gpio_is_valid(info->gpio_nstby)) - gpio_free(info->gpio_nstby); - - media_entity_cleanup(&sd->entity); - kfree(info); - return 0; -} - -static const struct i2c_device_id noon010_id[] = { - { MODULE_NAME, 0 }, - { }, -}; -MODULE_DEVICE_TABLE(i2c, noon010_id); - - -static struct i2c_driver noon010_i2c_driver = { - .driver = { - .name = MODULE_NAME - }, - .probe = noon010_probe, - .remove = noon010_remove, - .id_table = noon010_id, -}; - -module_i2c_driver(noon010_i2c_driver); - -MODULE_DESCRIPTION("Siliconfile NOON010PC30 camera driver"); -MODULE_AUTHOR("Sylwester Nawrocki "); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c deleted file mode 100644 index e7c82b29751..00000000000 --- a/drivers/media/video/ov7670.c +++ /dev/null @@ -1,1586 +0,0 @@ -/* - * A V4L2 driver for OmniVision OV7670 cameras. - * - * Copyright 2006 One Laptop Per Child Association, Inc. Written - * by Jonathan Corbet with substantial inspiration from Mark - * McClelland's ovcamchip code. - * - * Copyright 2006-7 Jonathan Corbet - * - * This file may be distributed under the terms of the GNU General - * Public License, version 2. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_AUTHOR("Jonathan Corbet "); -MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors"); -MODULE_LICENSE("GPL"); - -static bool debug; -module_param(debug, bool, 0644); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - -/* - * Basic window sizes. These probably belong somewhere more globally - * useful. - */ -#define VGA_WIDTH 640 -#define VGA_HEIGHT 480 -#define QVGA_WIDTH 320 -#define QVGA_HEIGHT 240 -#define CIF_WIDTH 352 -#define CIF_HEIGHT 288 -#define QCIF_WIDTH 176 -#define QCIF_HEIGHT 144 - -/* - * The 7670 sits on i2c with ID 0x42 - */ -#define OV7670_I2C_ADDR 0x42 - -/* Registers */ -#define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ -#define REG_BLUE 0x01 /* blue gain */ -#define REG_RED 0x02 /* red gain */ -#define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ -#define REG_COM1 0x04 /* Control 1 */ -#define COM1_CCIR656 0x40 /* CCIR656 enable */ -#define REG_BAVE 0x05 /* U/B Average level */ -#define REG_GbAVE 0x06 /* Y/Gb Average level */ -#define REG_AECHH 0x07 /* AEC MS 5 bits */ -#define REG_RAVE 0x08 /* V/R Average level */ -#define REG_COM2 0x09 /* Control 2 */ -#define COM2_SSLEEP 0x10 /* Soft sleep mode */ -#define REG_PID 0x0a /* Product ID MSB */ -#define REG_VER 0x0b /* Product ID LSB */ -#define REG_COM3 0x0c /* Control 3 */ -#define COM3_SWAP 0x40 /* Byte swap */ -#define COM3_SCALEEN 0x08 /* Enable scaling */ -#define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */ -#define REG_COM4 0x0d /* Control 4 */ -#define REG_COM5 0x0e /* All "reserved" */ -#define REG_COM6 0x0f /* Control 6 */ -#define REG_AECH 0x10 /* More bits of AEC value */ -#define REG_CLKRC 0x11 /* Clocl control */ -#define CLK_EXT 0x40 /* Use external clock directly */ -#define CLK_SCALE 0x3f /* Mask for internal clock scale */ -#define REG_COM7 0x12 /* Control 7 */ -#define COM7_RESET 0x80 /* Register reset */ -#define COM7_FMT_MASK 0x38 -#define COM7_FMT_VGA 0x00 -#define COM7_FMT_CIF 0x20 /* CIF format */ -#define COM7_FMT_QVGA 0x10 /* QVGA format */ -#define COM7_FMT_QCIF 0x08 /* QCIF format */ -#define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */ -#define COM7_YUV 0x00 /* YUV */ -#define COM7_BAYER 0x01 /* Bayer format */ -#define COM7_PBAYER 0x05 /* "Processed bayer" */ -#define REG_COM8 0x13 /* Control 8 */ -#define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ -#define COM8_AECSTEP 0x40 /* Unlimited AEC step size */ -#define COM8_BFILT 0x20 /* Band filter enable */ -#define COM8_AGC 0x04 /* Auto gain enable */ -#define COM8_AWB 0x02 /* White balance enable */ -#define COM8_AEC 0x01 /* Auto exposure enable */ -#define REG_COM9 0x14 /* Control 9 - gain ceiling */ -#define REG_COM10 0x15 /* Control 10 */ -#define COM10_HSYNC 0x40 /* HSYNC instead of HREF */ -#define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */ -#define COM10_HREF_REV 0x08 /* Reverse HREF */ -#define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */ -#define COM10_VS_NEG 0x02 /* VSYNC negative */ -#define COM10_HS_NEG 0x01 /* HSYNC negative */ -#define REG_HSTART 0x17 /* Horiz start high bits */ -#define REG_HSTOP 0x18 /* Horiz stop high bits */ -#define REG_VSTART 0x19 /* Vert start high bits */ -#define REG_VSTOP 0x1a /* Vert stop high bits */ -#define REG_PSHFT 0x1b /* Pixel delay after HREF */ -#define REG_MIDH 0x1c /* Manuf. ID high */ -#define REG_MIDL 0x1d /* Manuf. ID low */ -#define REG_MVFP 0x1e /* Mirror / vflip */ -#define MVFP_MIRROR 0x20 /* Mirror image */ -#define MVFP_FLIP 0x10 /* Vertical flip */ - -#define REG_AEW 0x24 /* AGC upper limit */ -#define REG_AEB 0x25 /* AGC lower limit */ -#define REG_VPT 0x26 /* AGC/AEC fast mode op region */ -#define REG_HSYST 0x30 /* HSYNC rising edge delay */ -#define REG_HSYEN 0x31 /* HSYNC falling edge delay */ -#define REG_HREF 0x32 /* HREF pieces */ -#define REG_TSLB 0x3a /* lots of stuff */ -#define TSLB_YLAST 0x04 /* UYVY or VYUY - see com13 */ -#define REG_COM11 0x3b /* Control 11 */ -#define COM11_NIGHT 0x80 /* NIght mode enable */ -#define COM11_NMFR 0x60 /* Two bit NM frame rate */ -#define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ -#define COM11_50HZ 0x08 /* Manual 50Hz select */ -#define COM11_EXP 0x02 -#define REG_COM12 0x3c /* Control 12 */ -#define COM12_HREF 0x80 /* HREF always */ -#define REG_COM13 0x3d /* Control 13 */ -#define COM13_GAMMA 0x80 /* Gamma enable */ -#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ -#define COM13_UVSWAP 0x01 /* V before U - w/TSLB */ -#define REG_COM14 0x3e /* Control 14 */ -#define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */ -#define REG_EDGE 0x3f /* Edge enhancement factor */ -#define REG_COM15 0x40 /* Control 15 */ -#define COM15_R10F0 0x00 /* Data range 10 to F0 */ -#define COM15_R01FE 0x80 /* 01 to FE */ -#define COM15_R00FF 0xc0 /* 00 to FF */ -#define COM15_RGB565 0x10 /* RGB565 output */ -#define COM15_RGB555 0x30 /* RGB555 output */ -#define REG_COM16 0x41 /* Control 16 */ -#define COM16_AWBGAIN 0x08 /* AWB gain enable */ -#define REG_COM17 0x42 /* Control 17 */ -#define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */ -#define COM17_CBAR 0x08 /* DSP Color bar */ - -/* - * This matrix defines how the colors are generated, must be - * tweaked to adjust hue and saturation. - * - * Order: v-red, v-green, v-blue, u-red, u-green, u-blue - * - * They are nine-bit signed quantities, with the sign bit - * stored in 0x58. Sign for v-red is bit 0, and up from there. - */ -#define REG_CMATRIX_BASE 0x4f -#define CMATRIX_LEN 6 -#define REG_CMATRIX_SIGN 0x58 - - -#define REG_BRIGHT 0x55 /* Brightness */ -#define REG_CONTRAS 0x56 /* Contrast control */ - -#define REG_GFIX 0x69 /* Fix gain control */ - -#define REG_REG76 0x76 /* OV's name */ -#define R76_BLKPCOR 0x80 /* Black pixel correction enable */ -#define R76_WHTPCOR 0x40 /* White pixel correction enable */ - -#define REG_RGB444 0x8c /* RGB 444 control */ -#define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */ -#define R444_RGBX 0x01 /* Empty nibble at end */ - -#define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */ -#define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ - -#define REG_BD50MAX 0xa5 /* 50hz banding step limit */ -#define REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */ -#define REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */ -#define REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */ -#define REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */ -#define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ -#define REG_BD60MAX 0xab /* 60hz banding step limit */ - - -/* - * Information we maintain about a known sensor. - */ -struct ov7670_format_struct; /* coming later */ -struct ov7670_info { - struct v4l2_subdev sd; - struct ov7670_format_struct *fmt; /* Current format */ - unsigned char sat; /* Saturation value */ - int hue; /* Hue value */ - int min_width; /* Filter out smaller sizes */ - int min_height; /* Filter out smaller sizes */ - int clock_speed; /* External clock speed (MHz) */ - u8 clkrc; /* Clock divider value */ - bool use_smbus; /* Use smbus I/O instead of I2C */ -}; - -static inline struct ov7670_info *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct ov7670_info, sd); -} - - - -/* - * The default register settings, as obtained from OmniVision. There - * is really no making sense of most of these - lots of "reserved" values - * and such. - * - * These settings give VGA YUYV. - */ - -struct regval_list { - unsigned char reg_num; - unsigned char value; -}; - -static struct regval_list ov7670_default_regs[] = { - { REG_COM7, COM7_RESET }, -/* - * Clock scale: 3 = 15fps - * 2 = 20fps - * 1 = 30fps - */ - { REG_CLKRC, 0x1 }, /* OV: clock scale (30 fps) */ - { REG_TSLB, 0x04 }, /* OV */ - { REG_COM7, 0 }, /* VGA */ - /* - * Set the hardware window. These values from OV don't entirely - * make sense - hstop is less than hstart. But they work... - */ - { REG_HSTART, 0x13 }, { REG_HSTOP, 0x01 }, - { REG_HREF, 0xb6 }, { REG_VSTART, 0x02 }, - { REG_VSTOP, 0x7a }, { REG_VREF, 0x0a }, - - { REG_COM3, 0 }, { REG_COM14, 0 }, - /* Mystery scaling numbers */ - { 0x70, 0x3a }, { 0x71, 0x35 }, - { 0x72, 0x11 }, { 0x73, 0xf0 }, - { 0xa2, 0x02 }, { REG_COM10, 0x0 }, - - /* Gamma curve values */ - { 0x7a, 0x20 }, { 0x7b, 0x10 }, - { 0x7c, 0x1e }, { 0x7d, 0x35 }, - { 0x7e, 0x5a }, { 0x7f, 0x69 }, - { 0x80, 0x76 }, { 0x81, 0x80 }, - { 0x82, 0x88 }, { 0x83, 0x8f }, - { 0x84, 0x96 }, { 0x85, 0xa3 }, - { 0x86, 0xaf }, { 0x87, 0xc4 }, - { 0x88, 0xd7 }, { 0x89, 0xe8 }, - - /* AGC and AEC parameters. Note we start by disabling those features, - then turn them only after tweaking the values. */ - { REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT }, - { REG_GAIN, 0 }, { REG_AECH, 0 }, - { REG_COM4, 0x40 }, /* magic reserved bit */ - { REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */ - { REG_BD50MAX, 0x05 }, { REG_BD60MAX, 0x07 }, - { REG_AEW, 0x95 }, { REG_AEB, 0x33 }, - { REG_VPT, 0xe3 }, { REG_HAECC1, 0x78 }, - { REG_HAECC2, 0x68 }, { 0xa1, 0x03 }, /* magic */ - { REG_HAECC3, 0xd8 }, { REG_HAECC4, 0xd8 }, - { REG_HAECC5, 0xf0 }, { REG_HAECC6, 0x90 }, - { REG_HAECC7, 0x94 }, - { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC }, - - /* Almost all of these are magic "reserved" values. */ - { REG_COM5, 0x61 }, { REG_COM6, 0x4b }, - { 0x16, 0x02 }, { REG_MVFP, 0x07 }, - { 0x21, 0x02 }, { 0x22, 0x91 }, - { 0x29, 0x07 }, { 0x33, 0x0b }, - { 0x35, 0x0b }, { 0x37, 0x1d }, - { 0x38, 0x71 }, { 0x39, 0x2a }, - { REG_COM12, 0x78 }, { 0x4d, 0x40 }, - { 0x4e, 0x20 }, { REG_GFIX, 0 }, - { 0x6b, 0x4a }, { 0x74, 0x10 }, - { 0x8d, 0x4f }, { 0x8e, 0 }, - { 0x8f, 0 }, { 0x90, 0 }, - { 0x91, 0 }, { 0x96, 0 }, - { 0x9a, 0 }, { 0xb0, 0x84 }, - { 0xb1, 0x0c }, { 0xb2, 0x0e }, - { 0xb3, 0x82 }, { 0xb8, 0x0a }, - - /* More reserved magic, some of which tweaks white balance */ - { 0x43, 0x0a }, { 0x44, 0xf0 }, - { 0x45, 0x34 }, { 0x46, 0x58 }, - { 0x47, 0x28 }, { 0x48, 0x3a }, - { 0x59, 0x88 }, { 0x5a, 0x88 }, - { 0x5b, 0x44 }, { 0x5c, 0x67 }, - { 0x5d, 0x49 }, { 0x5e, 0x0e }, - { 0x6c, 0x0a }, { 0x6d, 0x55 }, - { 0x6e, 0x11 }, { 0x6f, 0x9f }, /* "9e for advance AWB" */ - { 0x6a, 0x40 }, { REG_BLUE, 0x40 }, - { REG_RED, 0x60 }, - { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC|COM8_AWB }, - - /* Matrix coefficients */ - { 0x4f, 0x80 }, { 0x50, 0x80 }, - { 0x51, 0 }, { 0x52, 0x22 }, - { 0x53, 0x5e }, { 0x54, 0x80 }, - { 0x58, 0x9e }, - - { REG_COM16, COM16_AWBGAIN }, { REG_EDGE, 0 }, - { 0x75, 0x05 }, { 0x76, 0xe1 }, - { 0x4c, 0 }, { 0x77, 0x01 }, - { REG_COM13, 0xc3 }, { 0x4b, 0x09 }, - { 0xc9, 0x60 }, { REG_COM16, 0x38 }, - { 0x56, 0x40 }, - - { 0x34, 0x11 }, { REG_COM11, COM11_EXP|COM11_HZAUTO }, - { 0xa4, 0x88 }, { 0x96, 0 }, - { 0x97, 0x30 }, { 0x98, 0x20 }, - { 0x99, 0x30 }, { 0x9a, 0x84 }, - { 0x9b, 0x29 }, { 0x9c, 0x03 }, - { 0x9d, 0x4c }, { 0x9e, 0x3f }, - { 0x78, 0x04 }, - - /* Extra-weird stuff. Some sort of multiplexor register */ - { 0x79, 0x01 }, { 0xc8, 0xf0 }, - { 0x79, 0x0f }, { 0xc8, 0x00 }, - { 0x79, 0x10 }, { 0xc8, 0x7e }, - { 0x79, 0x0a }, { 0xc8, 0x80 }, - { 0x79, 0x0b }, { 0xc8, 0x01 }, - { 0x79, 0x0c }, { 0xc8, 0x0f }, - { 0x79, 0x0d }, { 0xc8, 0x20 }, - { 0x79, 0x09 }, { 0xc8, 0x80 }, - { 0x79, 0x02 }, { 0xc8, 0xc0 }, - { 0x79, 0x03 }, { 0xc8, 0x40 }, - { 0x79, 0x05 }, { 0xc8, 0x30 }, - { 0x79, 0x26 }, - - { 0xff, 0xff }, /* END MARKER */ -}; - - -/* - * Here we'll try to encapsulate the changes for just the output - * video format. - * - * RGB656 and YUV422 come from OV; RGB444 is homebrewed. - * - * IMPORTANT RULE: the first entry must be for COM7, see ov7670_s_fmt for why. - */ - - -static struct regval_list ov7670_fmt_yuv422[] = { - { REG_COM7, 0x0 }, /* Selects YUV mode */ - { REG_RGB444, 0 }, /* No RGB444 please */ - { REG_COM1, 0 }, /* CCIR601 */ - { REG_COM15, COM15_R00FF }, - { REG_COM9, 0x18 }, /* 4x gain ceiling; 0x8 is reserved bit */ - { 0x4f, 0x80 }, /* "matrix coefficient 1" */ - { 0x50, 0x80 }, /* "matrix coefficient 2" */ - { 0x51, 0 }, /* vb */ - { 0x52, 0x22 }, /* "matrix coefficient 4" */ - { 0x53, 0x5e }, /* "matrix coefficient 5" */ - { 0x54, 0x80 }, /* "matrix coefficient 6" */ - { REG_COM13, COM13_GAMMA|COM13_UVSAT }, - { 0xff, 0xff }, -}; - -static struct regval_list ov7670_fmt_rgb565[] = { - { REG_COM7, COM7_RGB }, /* Selects RGB mode */ - { REG_RGB444, 0 }, /* No RGB444 please */ - { REG_COM1, 0x0 }, /* CCIR601 */ - { REG_COM15, COM15_RGB565 }, - { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ - { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ - { 0x50, 0xb3 }, /* "matrix coefficient 2" */ - { 0x51, 0 }, /* vb */ - { 0x52, 0x3d }, /* "matrix coefficient 4" */ - { 0x53, 0xa7 }, /* "matrix coefficient 5" */ - { 0x54, 0xe4 }, /* "matrix coefficient 6" */ - { REG_COM13, COM13_GAMMA|COM13_UVSAT }, - { 0xff, 0xff }, -}; - -static struct regval_list ov7670_fmt_rgb444[] = { - { REG_COM7, COM7_RGB }, /* Selects RGB mode */ - { REG_RGB444, R444_ENABLE }, /* Enable xxxxrrrr ggggbbbb */ - { REG_COM1, 0x0 }, /* CCIR601 */ - { REG_COM15, COM15_R01FE|COM15_RGB565 }, /* Data range needed? */ - { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ - { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ - { 0x50, 0xb3 }, /* "matrix coefficient 2" */ - { 0x51, 0 }, /* vb */ - { 0x52, 0x3d }, /* "matrix coefficient 4" */ - { 0x53, 0xa7 }, /* "matrix coefficient 5" */ - { 0x54, 0xe4 }, /* "matrix coefficient 6" */ - { REG_COM13, COM13_GAMMA|COM13_UVSAT|0x2 }, /* Magic rsvd bit */ - { 0xff, 0xff }, -}; - -static struct regval_list ov7670_fmt_raw[] = { - { REG_COM7, COM7_BAYER }, - { REG_COM13, 0x08 }, /* No gamma, magic rsvd bit */ - { REG_COM16, 0x3d }, /* Edge enhancement, denoise */ - { REG_REG76, 0xe1 }, /* Pix correction, magic rsvd */ - { 0xff, 0xff }, -}; - - - -/* - * Low-level register I/O. - * - * Note that there are two versions of these. On the XO 1, the - * i2c controller only does SMBUS, so that's what we use. The - * ov7670 is not really an SMBUS device, though, so the communication - * is not always entirely reliable. - */ -static int ov7670_read_smbus(struct v4l2_subdev *sd, unsigned char reg, - unsigned char *value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - ret = i2c_smbus_read_byte_data(client, reg); - if (ret >= 0) { - *value = (unsigned char)ret; - ret = 0; - } - return ret; -} - - -static int ov7670_write_smbus(struct v4l2_subdev *sd, unsigned char reg, - unsigned char value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret = i2c_smbus_write_byte_data(client, reg, value); - - if (reg == REG_COM7 && (value & COM7_RESET)) - msleep(5); /* Wait for reset to run */ - return ret; -} - -/* - * On most platforms, we'd rather do straight i2c I/O. - */ -static int ov7670_read_i2c(struct v4l2_subdev *sd, unsigned char reg, - unsigned char *value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 data = reg; - struct i2c_msg msg; - int ret; - - /* - * Send out the register address... - */ - msg.addr = client->addr; - msg.flags = 0; - msg.len = 1; - msg.buf = &data; - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret < 0) { - printk(KERN_ERR "Error %d on register write\n", ret); - return ret; - } - /* - * ...then read back the result. - */ - msg.flags = I2C_M_RD; - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret >= 0) { - *value = data; - ret = 0; - } - return ret; -} - - -static int ov7670_write_i2c(struct v4l2_subdev *sd, unsigned char reg, - unsigned char value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct i2c_msg msg; - unsigned char data[2] = { reg, value }; - int ret; - - msg.addr = client->addr; - msg.flags = 0; - msg.len = 2; - msg.buf = data; - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret > 0) - ret = 0; - if (reg == REG_COM7 && (value & COM7_RESET)) - msleep(5); /* Wait for reset to run */ - return ret; -} - -static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, - unsigned char *value) -{ - struct ov7670_info *info = to_state(sd); - if (info->use_smbus) - return ov7670_read_smbus(sd, reg, value); - else - return ov7670_read_i2c(sd, reg, value); -} - -static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, - unsigned char value) -{ - struct ov7670_info *info = to_state(sd); - if (info->use_smbus) - return ov7670_write_smbus(sd, reg, value); - else - return ov7670_write_i2c(sd, reg, value); -} - -/* - * Write a list of register settings; ff/ff stops the process. - */ -static int ov7670_write_array(struct v4l2_subdev *sd, struct regval_list *vals) -{ - while (vals->reg_num != 0xff || vals->value != 0xff) { - int ret = ov7670_write(sd, vals->reg_num, vals->value); - if (ret < 0) - return ret; - vals++; - } - return 0; -} - - -/* - * Stuff that knows about the sensor. - */ -static int ov7670_reset(struct v4l2_subdev *sd, u32 val) -{ - ov7670_write(sd, REG_COM7, COM7_RESET); - msleep(1); - return 0; -} - - -static int ov7670_init(struct v4l2_subdev *sd, u32 val) -{ - return ov7670_write_array(sd, ov7670_default_regs); -} - - - -static int ov7670_detect(struct v4l2_subdev *sd) -{ - unsigned char v; - int ret; - - ret = ov7670_init(sd, 0); - if (ret < 0) - return ret; - ret = ov7670_read(sd, REG_MIDH, &v); - if (ret < 0) - return ret; - if (v != 0x7f) /* OV manuf. id. */ - return -ENODEV; - ret = ov7670_read(sd, REG_MIDL, &v); - if (ret < 0) - return ret; - if (v != 0xa2) - return -ENODEV; - /* - * OK, we know we have an OmniVision chip...but which one? - */ - ret = ov7670_read(sd, REG_PID, &v); - if (ret < 0) - return ret; - if (v != 0x76) /* PID + VER = 0x76 / 0x73 */ - return -ENODEV; - ret = ov7670_read(sd, REG_VER, &v); - if (ret < 0) - return ret; - if (v != 0x73) /* PID + VER = 0x76 / 0x73 */ - return -ENODEV; - return 0; -} - - -/* - * Store information about the video data format. The color matrix - * is deeply tied into the format, so keep the relevant values here. - * The magic matrix numbers come from OmniVision. - */ -static struct ov7670_format_struct { - enum v4l2_mbus_pixelcode mbus_code; - enum v4l2_colorspace colorspace; - struct regval_list *regs; - int cmatrix[CMATRIX_LEN]; -} ov7670_formats[] = { - { - .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .regs = ov7670_fmt_yuv422, - .cmatrix = { 128, -128, 0, -34, -94, 128 }, - }, - { - .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE, - .colorspace = V4L2_COLORSPACE_SRGB, - .regs = ov7670_fmt_rgb444, - .cmatrix = { 179, -179, 0, -61, -176, 228 }, - }, - { - .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE, - .colorspace = V4L2_COLORSPACE_SRGB, - .regs = ov7670_fmt_rgb565, - .cmatrix = { 179, -179, 0, -61, -176, 228 }, - }, - { - .mbus_code = V4L2_MBUS_FMT_SBGGR8_1X8, - .colorspace = V4L2_COLORSPACE_SRGB, - .regs = ov7670_fmt_raw, - .cmatrix = { 0, 0, 0, 0, 0, 0 }, - }, -}; -#define N_OV7670_FMTS ARRAY_SIZE(ov7670_formats) - - -/* - * Then there is the issue of window sizes. Try to capture the info here. - */ - -/* - * QCIF mode is done (by OV) in a very strange way - it actually looks like - * VGA with weird scaling options - they do *not* use the canned QCIF mode - * which is allegedly provided by the sensor. So here's the weird register - * settings. - */ -static struct regval_list ov7670_qcif_regs[] = { - { REG_COM3, COM3_SCALEEN|COM3_DCWEN }, - { REG_COM3, COM3_DCWEN }, - { REG_COM14, COM14_DCWEN | 0x01}, - { 0x73, 0xf1 }, - { 0xa2, 0x52 }, - { 0x7b, 0x1c }, - { 0x7c, 0x28 }, - { 0x7d, 0x3c }, - { 0x7f, 0x69 }, - { REG_COM9, 0x38 }, - { 0xa1, 0x0b }, - { 0x74, 0x19 }, - { 0x9a, 0x80 }, - { 0x43, 0x14 }, - { REG_COM13, 0xc0 }, - { 0xff, 0xff }, -}; - -static struct ov7670_win_size { - int width; - int height; - unsigned char com7_bit; - int hstart; /* Start/stop values for the camera. Note */ - int hstop; /* that they do not always make complete */ - int vstart; /* sense to humans, but evidently the sensor */ - int vstop; /* will do the right thing... */ - struct regval_list *regs; /* Regs to tweak */ -/* h/vref stuff */ -} ov7670_win_sizes[] = { - /* VGA */ - { - .width = VGA_WIDTH, - .height = VGA_HEIGHT, - .com7_bit = COM7_FMT_VGA, - .hstart = 158, /* These values from */ - .hstop = 14, /* Omnivision */ - .vstart = 10, - .vstop = 490, - .regs = NULL, - }, - /* CIF */ - { - .width = CIF_WIDTH, - .height = CIF_HEIGHT, - .com7_bit = COM7_FMT_CIF, - .hstart = 170, /* Empirically determined */ - .hstop = 90, - .vstart = 14, - .vstop = 494, - .regs = NULL, - }, - /* QVGA */ - { - .width = QVGA_WIDTH, - .height = QVGA_HEIGHT, - .com7_bit = COM7_FMT_QVGA, - .hstart = 168, /* Empirically determined */ - .hstop = 24, - .vstart = 12, - .vstop = 492, - .regs = NULL, - }, - /* QCIF */ - { - .width = QCIF_WIDTH, - .height = QCIF_HEIGHT, - .com7_bit = COM7_FMT_VGA, /* see comment above */ - .hstart = 456, /* Empirically determined */ - .hstop = 24, - .vstart = 14, - .vstop = 494, - .regs = ov7670_qcif_regs, - }, -}; - -#define N_WIN_SIZES (ARRAY_SIZE(ov7670_win_sizes)) - - -/* - * Store a set of start/stop values into the camera. - */ -static int ov7670_set_hw(struct v4l2_subdev *sd, int hstart, int hstop, - int vstart, int vstop) -{ - int ret; - unsigned char v; -/* - * Horizontal: 11 bits, top 8 live in hstart and hstop. Bottom 3 of - * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is - * a mystery "edge offset" value in the top two bits of href. - */ - ret = ov7670_write(sd, REG_HSTART, (hstart >> 3) & 0xff); - ret += ov7670_write(sd, REG_HSTOP, (hstop >> 3) & 0xff); - ret += ov7670_read(sd, REG_HREF, &v); - v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7); - msleep(10); - ret += ov7670_write(sd, REG_HREF, v); -/* - * Vertical: similar arrangement, but only 10 bits. - */ - ret += ov7670_write(sd, REG_VSTART, (vstart >> 2) & 0xff); - ret += ov7670_write(sd, REG_VSTOP, (vstop >> 2) & 0xff); - ret += ov7670_read(sd, REG_VREF, &v); - v = (v & 0xf0) | ((vstop & 0x3) << 2) | (vstart & 0x3); - msleep(10); - ret += ov7670_write(sd, REG_VREF, v); - return ret; -} - - -static int ov7670_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, - enum v4l2_mbus_pixelcode *code) -{ - if (index >= N_OV7670_FMTS) - return -EINVAL; - - *code = ov7670_formats[index].mbus_code; - return 0; -} - -static int ov7670_try_fmt_internal(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt, - struct ov7670_format_struct **ret_fmt, - struct ov7670_win_size **ret_wsize) -{ - int index; - struct ov7670_win_size *wsize; - - for (index = 0; index < N_OV7670_FMTS; index++) - if (ov7670_formats[index].mbus_code == fmt->code) - break; - if (index >= N_OV7670_FMTS) { - /* default to first format */ - index = 0; - fmt->code = ov7670_formats[0].mbus_code; - } - if (ret_fmt != NULL) - *ret_fmt = ov7670_formats + index; - /* - * Fields: the OV devices claim to be progressive. - */ - fmt->field = V4L2_FIELD_NONE; - /* - * Round requested image size down to the nearest - * we support, but not below the smallest. - */ - for (wsize = ov7670_win_sizes; wsize < ov7670_win_sizes + N_WIN_SIZES; - wsize++) - if (fmt->width >= wsize->width && fmt->height >= wsize->height) - break; - if (wsize >= ov7670_win_sizes + N_WIN_SIZES) - wsize--; /* Take the smallest one */ - if (ret_wsize != NULL) - *ret_wsize = wsize; - /* - * Note the size we'll actually handle. - */ - fmt->width = wsize->width; - fmt->height = wsize->height; - fmt->colorspace = ov7670_formats[index].colorspace; - return 0; -} - -static int ov7670_try_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) -{ - return ov7670_try_fmt_internal(sd, fmt, NULL, NULL); -} - -/* - * Set a format. - */ -static int ov7670_s_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) -{ - struct ov7670_format_struct *ovfmt; - struct ov7670_win_size *wsize; - struct ov7670_info *info = to_state(sd); - unsigned char com7; - int ret; - - ret = ov7670_try_fmt_internal(sd, fmt, &ovfmt, &wsize); - - if (ret) - return ret; - /* - * COM7 is a pain in the ass, it doesn't like to be read then - * quickly written afterward. But we have everything we need - * to set it absolutely here, as long as the format-specific - * register sets list it first. - */ - com7 = ovfmt->regs[0].value; - com7 |= wsize->com7_bit; - ov7670_write(sd, REG_COM7, com7); - /* - * Now write the rest of the array. Also store start/stops - */ - ov7670_write_array(sd, ovfmt->regs + 1); - ov7670_set_hw(sd, wsize->hstart, wsize->hstop, wsize->vstart, - wsize->vstop); - ret = 0; - if (wsize->regs) - ret = ov7670_write_array(sd, wsize->regs); - info->fmt = ovfmt; - - /* - * If we're running RGB565, we must rewrite clkrc after setting - * the other parameters or the image looks poor. If we're *not* - * doing RGB565, we must not rewrite clkrc or the image looks - * *really* poor. - * - * (Update) Now that we retain clkrc state, we should be able - * to write it unconditionally, and that will make the frame - * rate persistent too. - */ - if (ret == 0) - ret = ov7670_write(sd, REG_CLKRC, info->clkrc); - return 0; -} - -/* - * Implement G/S_PARM. There is a "high quality" mode we could try - * to do someday; for now, we just do the frame rate tweak. - */ -static int ov7670_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) -{ - struct v4l2_captureparm *cp = &parms->parm.capture; - struct ov7670_info *info = to_state(sd); - - if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - memset(cp, 0, sizeof(struct v4l2_captureparm)); - cp->capability = V4L2_CAP_TIMEPERFRAME; - cp->timeperframe.numerator = 1; - cp->timeperframe.denominator = info->clock_speed; - if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1) - cp->timeperframe.denominator /= (info->clkrc & CLK_SCALE); - return 0; -} - -static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) -{ - struct v4l2_captureparm *cp = &parms->parm.capture; - struct v4l2_fract *tpf = &cp->timeperframe; - struct ov7670_info *info = to_state(sd); - int div; - - if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (cp->extendedmode != 0) - return -EINVAL; - - if (tpf->numerator == 0 || tpf->denominator == 0) - div = 1; /* Reset to full rate */ - else - div = (tpf->numerator * info->clock_speed) / tpf->denominator; - if (div == 0) - div = 1; - else if (div > CLK_SCALE) - div = CLK_SCALE; - info->clkrc = (info->clkrc & 0x80) | div; - tpf->numerator = 1; - tpf->denominator = info->clock_speed / div; - return ov7670_write(sd, REG_CLKRC, info->clkrc); -} - - -/* - * Frame intervals. Since frame rates are controlled with the clock - * divider, we can only do 30/n for integer n values. So no continuous - * or stepwise options. Here we just pick a handful of logical values. - */ - -static int ov7670_frame_rates[] = { 30, 15, 10, 5, 1 }; - -static int ov7670_enum_frameintervals(struct v4l2_subdev *sd, - struct v4l2_frmivalenum *interval) -{ - if (interval->index >= ARRAY_SIZE(ov7670_frame_rates)) - return -EINVAL; - interval->type = V4L2_FRMIVAL_TYPE_DISCRETE; - interval->discrete.numerator = 1; - interval->discrete.denominator = ov7670_frame_rates[interval->index]; - return 0; -} - -/* - * Frame size enumeration - */ -static int ov7670_enum_framesizes(struct v4l2_subdev *sd, - struct v4l2_frmsizeenum *fsize) -{ - struct ov7670_info *info = to_state(sd); - int i; - int num_valid = -1; - __u32 index = fsize->index; - - /* - * If a minimum width/height was requested, filter out the capture - * windows that fall outside that. - */ - for (i = 0; i < N_WIN_SIZES; i++) { - struct ov7670_win_size *win = &ov7670_win_sizes[index]; - if (info->min_width && win->width < info->min_width) - continue; - if (info->min_height && win->height < info->min_height) - continue; - if (index == ++num_valid) { - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = win->width; - fsize->discrete.height = win->height; - return 0; - } - } - - return -EINVAL; -} - -/* - * Code for dealing with controls. - */ - -static int ov7670_store_cmatrix(struct v4l2_subdev *sd, - int matrix[CMATRIX_LEN]) -{ - int i, ret; - unsigned char signbits = 0; - - /* - * Weird crap seems to exist in the upper part of - * the sign bits register, so let's preserve it. - */ - ret = ov7670_read(sd, REG_CMATRIX_SIGN, &signbits); - signbits &= 0xc0; - - for (i = 0; i < CMATRIX_LEN; i++) { - unsigned char raw; - - if (matrix[i] < 0) { - signbits |= (1 << i); - if (matrix[i] < -255) - raw = 0xff; - else - raw = (-1 * matrix[i]) & 0xff; - } - else { - if (matrix[i] > 255) - raw = 0xff; - else - raw = matrix[i] & 0xff; - } - ret += ov7670_write(sd, REG_CMATRIX_BASE + i, raw); - } - ret += ov7670_write(sd, REG_CMATRIX_SIGN, signbits); - return ret; -} - - -/* - * Hue also requires messing with the color matrix. It also requires - * trig functions, which tend not to be well supported in the kernel. - * So here is a simple table of sine values, 0-90 degrees, in steps - * of five degrees. Values are multiplied by 1000. - * - * The following naive approximate trig functions require an argument - * carefully limited to -180 <= theta <= 180. - */ -#define SIN_STEP 5 -static const int ov7670_sin_table[] = { - 0, 87, 173, 258, 342, 422, - 499, 573, 642, 707, 766, 819, - 866, 906, 939, 965, 984, 996, - 1000 -}; - -static int ov7670_sine(int theta) -{ - int chs = 1; - int sine; - - if (theta < 0) { - theta = -theta; - chs = -1; - } - if (theta <= 90) - sine = ov7670_sin_table[theta/SIN_STEP]; - else { - theta -= 90; - sine = 1000 - ov7670_sin_table[theta/SIN_STEP]; - } - return sine*chs; -} - -static int ov7670_cosine(int theta) -{ - theta = 90 - theta; - if (theta > 180) - theta -= 360; - else if (theta < -180) - theta += 360; - return ov7670_sine(theta); -} - - - - -static void ov7670_calc_cmatrix(struct ov7670_info *info, - int matrix[CMATRIX_LEN]) -{ - int i; - /* - * Apply the current saturation setting first. - */ - for (i = 0; i < CMATRIX_LEN; i++) - matrix[i] = (info->fmt->cmatrix[i]*info->sat) >> 7; - /* - * Then, if need be, rotate the hue value. - */ - if (info->hue != 0) { - int sinth, costh, tmpmatrix[CMATRIX_LEN]; - - memcpy(tmpmatrix, matrix, CMATRIX_LEN*sizeof(int)); - sinth = ov7670_sine(info->hue); - costh = ov7670_cosine(info->hue); - - matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000; - matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000; - matrix[2] = (matrix[5]*sinth + matrix[2]*costh)/1000; - matrix[3] = (matrix[3]*costh - matrix[0]*sinth)/1000; - matrix[4] = (matrix[4]*costh - matrix[1]*sinth)/1000; - matrix[5] = (matrix[5]*costh - matrix[2]*sinth)/1000; - } -} - - - -static int ov7670_s_sat(struct v4l2_subdev *sd, int value) -{ - struct ov7670_info *info = to_state(sd); - int matrix[CMATRIX_LEN]; - int ret; - - info->sat = value; - ov7670_calc_cmatrix(info, matrix); - ret = ov7670_store_cmatrix(sd, matrix); - return ret; -} - -static int ov7670_g_sat(struct v4l2_subdev *sd, __s32 *value) -{ - struct ov7670_info *info = to_state(sd); - - *value = info->sat; - return 0; -} - -static int ov7670_s_hue(struct v4l2_subdev *sd, int value) -{ - struct ov7670_info *info = to_state(sd); - int matrix[CMATRIX_LEN]; - int ret; - - if (value < -180 || value > 180) - return -EINVAL; - info->hue = value; - ov7670_calc_cmatrix(info, matrix); - ret = ov7670_store_cmatrix(sd, matrix); - return ret; -} - - -static int ov7670_g_hue(struct v4l2_subdev *sd, __s32 *value) -{ - struct ov7670_info *info = to_state(sd); - - *value = info->hue; - return 0; -} - - -/* - * Some weird registers seem to store values in a sign/magnitude format! - */ -static unsigned char ov7670_sm_to_abs(unsigned char v) -{ - if ((v & 0x80) == 0) - return v + 128; - return 128 - (v & 0x7f); -} - - -static unsigned char ov7670_abs_to_sm(unsigned char v) -{ - if (v > 127) - return v & 0x7f; - return (128 - v) | 0x80; -} - -static int ov7670_s_brightness(struct v4l2_subdev *sd, int value) -{ - unsigned char com8 = 0, v; - int ret; - - ov7670_read(sd, REG_COM8, &com8); - com8 &= ~COM8_AEC; - ov7670_write(sd, REG_COM8, com8); - v = ov7670_abs_to_sm(value); - ret = ov7670_write(sd, REG_BRIGHT, v); - return ret; -} - -static int ov7670_g_brightness(struct v4l2_subdev *sd, __s32 *value) -{ - unsigned char v = 0; - int ret = ov7670_read(sd, REG_BRIGHT, &v); - - *value = ov7670_sm_to_abs(v); - return ret; -} - -static int ov7670_s_contrast(struct v4l2_subdev *sd, int value) -{ - return ov7670_write(sd, REG_CONTRAS, (unsigned char) value); -} - -static int ov7670_g_contrast(struct v4l2_subdev *sd, __s32 *value) -{ - unsigned char v = 0; - int ret = ov7670_read(sd, REG_CONTRAS, &v); - - *value = v; - return ret; -} - -static int ov7670_g_hflip(struct v4l2_subdev *sd, __s32 *value) -{ - int ret; - unsigned char v = 0; - - ret = ov7670_read(sd, REG_MVFP, &v); - *value = (v & MVFP_MIRROR) == MVFP_MIRROR; - return ret; -} - - -static int ov7670_s_hflip(struct v4l2_subdev *sd, int value) -{ - unsigned char v = 0; - int ret; - - ret = ov7670_read(sd, REG_MVFP, &v); - if (value) - v |= MVFP_MIRROR; - else - v &= ~MVFP_MIRROR; - msleep(10); /* FIXME */ - ret += ov7670_write(sd, REG_MVFP, v); - return ret; -} - - - -static int ov7670_g_vflip(struct v4l2_subdev *sd, __s32 *value) -{ - int ret; - unsigned char v = 0; - - ret = ov7670_read(sd, REG_MVFP, &v); - *value = (v & MVFP_FLIP) == MVFP_FLIP; - return ret; -} - - -static int ov7670_s_vflip(struct v4l2_subdev *sd, int value) -{ - unsigned char v = 0; - int ret; - - ret = ov7670_read(sd, REG_MVFP, &v); - if (value) - v |= MVFP_FLIP; - else - v &= ~MVFP_FLIP; - msleep(10); /* FIXME */ - ret += ov7670_write(sd, REG_MVFP, v); - return ret; -} - -/* - * GAIN is split between REG_GAIN and REG_VREF[7:6]. If one believes - * the data sheet, the VREF parts should be the most significant, but - * experience shows otherwise. There seems to be little value in - * messing with the VREF bits, so we leave them alone. - */ -static int ov7670_g_gain(struct v4l2_subdev *sd, __s32 *value) -{ - int ret; - unsigned char gain; - - ret = ov7670_read(sd, REG_GAIN, &gain); - *value = gain; - return ret; -} - -static int ov7670_s_gain(struct v4l2_subdev *sd, int value) -{ - int ret; - unsigned char com8; - - ret = ov7670_write(sd, REG_GAIN, value & 0xff); - /* Have to turn off AGC as well */ - if (ret == 0) { - ret = ov7670_read(sd, REG_COM8, &com8); - ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AGC); - } - return ret; -} - -/* - * Tweak autogain. - */ -static int ov7670_g_autogain(struct v4l2_subdev *sd, __s32 *value) -{ - int ret; - unsigned char com8; - - ret = ov7670_read(sd, REG_COM8, &com8); - *value = (com8 & COM8_AGC) != 0; - return ret; -} - -static int ov7670_s_autogain(struct v4l2_subdev *sd, int value) -{ - int ret; - unsigned char com8; - - ret = ov7670_read(sd, REG_COM8, &com8); - if (ret == 0) { - if (value) - com8 |= COM8_AGC; - else - com8 &= ~COM8_AGC; - ret = ov7670_write(sd, REG_COM8, com8); - } - return ret; -} - -/* - * Exposure is spread all over the place: top 6 bits in AECHH, middle - * 8 in AECH, and two stashed in COM1 just for the hell of it. - */ -static int ov7670_g_exp(struct v4l2_subdev *sd, __s32 *value) -{ - int ret; - unsigned char com1, aech, aechh; - - ret = ov7670_read(sd, REG_COM1, &com1) + - ov7670_read(sd, REG_AECH, &aech) + - ov7670_read(sd, REG_AECHH, &aechh); - *value = ((aechh & 0x3f) << 10) | (aech << 2) | (com1 & 0x03); - return ret; -} - -static int ov7670_s_exp(struct v4l2_subdev *sd, int value) -{ - int ret; - unsigned char com1, com8, aech, aechh; - - ret = ov7670_read(sd, REG_COM1, &com1) + - ov7670_read(sd, REG_COM8, &com8); - ov7670_read(sd, REG_AECHH, &aechh); - if (ret) - return ret; - - com1 = (com1 & 0xfc) | (value & 0x03); - aech = (value >> 2) & 0xff; - aechh = (aechh & 0xc0) | ((value >> 10) & 0x3f); - ret = ov7670_write(sd, REG_COM1, com1) + - ov7670_write(sd, REG_AECH, aech) + - ov7670_write(sd, REG_AECHH, aechh); - /* Have to turn off AEC as well */ - if (ret == 0) - ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AEC); - return ret; -} - -/* - * Tweak autoexposure. - */ -static int ov7670_g_autoexp(struct v4l2_subdev *sd, __s32 *value) -{ - int ret; - unsigned char com8; - enum v4l2_exposure_auto_type *atype = (enum v4l2_exposure_auto_type *) value; - - ret = ov7670_read(sd, REG_COM8, &com8); - if (com8 & COM8_AEC) - *atype = V4L2_EXPOSURE_AUTO; - else - *atype = V4L2_EXPOSURE_MANUAL; - return ret; -} - -static int ov7670_s_autoexp(struct v4l2_subdev *sd, - enum v4l2_exposure_auto_type value) -{ - int ret; - unsigned char com8; - - ret = ov7670_read(sd, REG_COM8, &com8); - if (ret == 0) { - if (value == V4L2_EXPOSURE_AUTO) - com8 |= COM8_AEC; - else - com8 &= ~COM8_AEC; - ret = ov7670_write(sd, REG_COM8, com8); - } - return ret; -} - - - -static int ov7670_queryctrl(struct v4l2_subdev *sd, - struct v4l2_queryctrl *qc) -{ - /* Fill in min, max, step and default value for these controls. */ - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); - case V4L2_CID_CONTRAST: - return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64); - case V4L2_CID_VFLIP: - case V4L2_CID_HFLIP: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); - case V4L2_CID_SATURATION: - return v4l2_ctrl_query_fill(qc, 0, 256, 1, 128); - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(qc, -180, 180, 5, 0); - case V4L2_CID_GAIN: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); - case V4L2_CID_AUTOGAIN: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); - case V4L2_CID_EXPOSURE: - return v4l2_ctrl_query_fill(qc, 0, 65535, 1, 500); - case V4L2_CID_EXPOSURE_AUTO: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); - } - return -EINVAL; -} - -static int ov7670_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - return ov7670_g_brightness(sd, &ctrl->value); - case V4L2_CID_CONTRAST: - return ov7670_g_contrast(sd, &ctrl->value); - case V4L2_CID_SATURATION: - return ov7670_g_sat(sd, &ctrl->value); - case V4L2_CID_HUE: - return ov7670_g_hue(sd, &ctrl->value); - case V4L2_CID_VFLIP: - return ov7670_g_vflip(sd, &ctrl->value); - case V4L2_CID_HFLIP: - return ov7670_g_hflip(sd, &ctrl->value); - case V4L2_CID_GAIN: - return ov7670_g_gain(sd, &ctrl->value); - case V4L2_CID_AUTOGAIN: - return ov7670_g_autogain(sd, &ctrl->value); - case V4L2_CID_EXPOSURE: - return ov7670_g_exp(sd, &ctrl->value); - case V4L2_CID_EXPOSURE_AUTO: - return ov7670_g_autoexp(sd, &ctrl->value); - } - return -EINVAL; -} - -static int ov7670_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - return ov7670_s_brightness(sd, ctrl->value); - case V4L2_CID_CONTRAST: - return ov7670_s_contrast(sd, ctrl->value); - case V4L2_CID_SATURATION: - return ov7670_s_sat(sd, ctrl->value); - case V4L2_CID_HUE: - return ov7670_s_hue(sd, ctrl->value); - case V4L2_CID_VFLIP: - return ov7670_s_vflip(sd, ctrl->value); - case V4L2_CID_HFLIP: - return ov7670_s_hflip(sd, ctrl->value); - case V4L2_CID_GAIN: - return ov7670_s_gain(sd, ctrl->value); - case V4L2_CID_AUTOGAIN: - return ov7670_s_autogain(sd, ctrl->value); - case V4L2_CID_EXPOSURE: - return ov7670_s_exp(sd, ctrl->value); - case V4L2_CID_EXPOSURE_AUTO: - return ov7670_s_autoexp(sd, - (enum v4l2_exposure_auto_type) ctrl->value); - } - return -EINVAL; -} - -static int ov7670_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_OV7670, 0); -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - unsigned char val = 0; - int ret; - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - ret = ov7670_read(sd, reg->reg & 0xff, &val); - reg->val = val; - reg->size = 1; - return ret; -} - -static int ov7670_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - ov7670_write(sd, reg->reg & 0xff, reg->val & 0xff); - return 0; -} -#endif - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops ov7670_core_ops = { - .g_chip_ident = ov7670_g_chip_ident, - .g_ctrl = ov7670_g_ctrl, - .s_ctrl = ov7670_s_ctrl, - .queryctrl = ov7670_queryctrl, - .reset = ov7670_reset, - .init = ov7670_init, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = ov7670_g_register, - .s_register = ov7670_s_register, -#endif -}; - -static const struct v4l2_subdev_video_ops ov7670_video_ops = { - .enum_mbus_fmt = ov7670_enum_mbus_fmt, - .try_mbus_fmt = ov7670_try_mbus_fmt, - .s_mbus_fmt = ov7670_s_mbus_fmt, - .s_parm = ov7670_s_parm, - .g_parm = ov7670_g_parm, - .enum_frameintervals = ov7670_enum_frameintervals, - .enum_framesizes = ov7670_enum_framesizes, -}; - -static const struct v4l2_subdev_ops ov7670_ops = { - .core = &ov7670_core_ops, - .video = &ov7670_video_ops, -}; - -/* ----------------------------------------------------------------------- */ - -static int ov7670_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct v4l2_subdev *sd; - struct ov7670_info *info; - int ret; - - info = kzalloc(sizeof(struct ov7670_info), GFP_KERNEL); - if (info == NULL) - return -ENOMEM; - sd = &info->sd; - v4l2_i2c_subdev_init(sd, client, &ov7670_ops); - - info->clock_speed = 30; /* default: a guess */ - if (client->dev.platform_data) { - struct ov7670_config *config = client->dev.platform_data; - - /* - * Must apply configuration before initializing device, because it - * selects I/O method. - */ - info->min_width = config->min_width; - info->min_height = config->min_height; - info->use_smbus = config->use_smbus; - - if (config->clock_speed) - info->clock_speed = config->clock_speed; - } - - /* Make sure it's an ov7670 */ - ret = ov7670_detect(sd); - if (ret) { - v4l_dbg(1, debug, client, - "chip found @ 0x%x (%s) is not an ov7670 chip.\n", - client->addr << 1, client->adapter->name); - kfree(info); - return ret; - } - v4l_info(client, "chip found @ 0x%02x (%s)\n", - client->addr << 1, client->adapter->name); - - info->fmt = &ov7670_formats[0]; - info->sat = 128; /* Review this */ - info->clkrc = info->clock_speed / 30; - return 0; -} - - -static int ov7670_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 ov7670_id[] = { - { "ov7670", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ov7670_id); - -static struct i2c_driver ov7670_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "ov7670", - }, - .probe = ov7670_probe, - .remove = ov7670_remove, - .id_table = ov7670_id, -}; - -module_i2c_driver(ov7670_driver); diff --git a/drivers/media/video/s5k6aa.c b/drivers/media/video/s5k6aa.c deleted file mode 100644 index 045ca7f4f6c..00000000000 --- a/drivers/media/video/s5k6aa.c +++ /dev/null @@ -1,1667 +0,0 @@ -/* - * Driver for Samsung S5K6AAFX SXGA 1/6" 1.3M CMOS Image Sensor - * with embedded SoC ISP. - * - * Copyright (C) 2011, Samsung Electronics Co., Ltd. - * Sylwester Nawrocki - * - * Based on a driver authored by Dongsoo Nathaniel Kim. - * Copyright (C) 2009, Dongsoo Nathaniel Kim - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -static int debug; -module_param(debug, int, 0644); - -#define DRIVER_NAME "S5K6AA" - -/* The token to indicate array termination */ -#define S5K6AA_TERM 0xffff -#define S5K6AA_OUT_WIDTH_DEF 640 -#define S5K6AA_OUT_HEIGHT_DEF 480 -#define S5K6AA_WIN_WIDTH_MAX 1280 -#define S5K6AA_WIN_HEIGHT_MAX 1024 -#define S5K6AA_WIN_WIDTH_MIN 8 -#define S5K6AA_WIN_HEIGHT_MIN 8 - -/* - * H/W register Interface (0xD0000000 - 0xD0000FFF) - */ -#define AHB_MSB_ADDR_PTR 0xfcfc -#define GEN_REG_OFFSH 0xd000 -#define REG_CMDWR_ADDRH 0x0028 -#define REG_CMDWR_ADDRL 0x002a -#define REG_CMDRD_ADDRH 0x002c -#define REG_CMDRD_ADDRL 0x002e -#define REG_CMDBUF0_ADDR 0x0f12 -#define REG_CMDBUF1_ADDR 0x0f10 - -/* - * Host S/W Register interface (0x70000000 - 0x70002000) - * The value of the two most significant address bytes is 0x7000, - * (HOST_SWIF_OFFS_H). The register addresses below specify 2 LSBs. - */ -#define HOST_SWIF_OFFSH 0x7000 - -/* Initialization parameters */ -/* Master clock frequency in KHz */ -#define REG_I_INCLK_FREQ_L 0x01b8 -#define REG_I_INCLK_FREQ_H 0x01ba -#define MIN_MCLK_FREQ_KHZ 6000U -#define MAX_MCLK_FREQ_KHZ 27000U -#define REG_I_USE_NPVI_CLOCKS 0x01c6 -#define REG_I_USE_NMIPI_CLOCKS 0x01c8 - -/* Clock configurations, n = 0..2. REG_I_* frequency unit is 4 kHz. */ -#define REG_I_OPCLK_4KHZ(n) ((n) * 6 + 0x01cc) -#define REG_I_MIN_OUTRATE_4KHZ(n) ((n) * 6 + 0x01ce) -#define REG_I_MAX_OUTRATE_4KHZ(n) ((n) * 6 + 0x01d0) -#define SYS_PLL_OUT_FREQ (48000000 / 4000) -#define PCLK_FREQ_MIN (24000000 / 4000) -#define PCLK_FREQ_MAX (48000000 / 4000) -#define REG_I_INIT_PARAMS_UPDATED 0x01e0 -#define REG_I_ERROR_INFO 0x01e2 - -/* General purpose parameters */ -#define REG_USER_BRIGHTNESS 0x01e4 -#define REG_USER_CONTRAST 0x01e6 -#define REG_USER_SATURATION 0x01e8 -#define REG_USER_SHARPBLUR 0x01ea - -#define REG_G_SPEC_EFFECTS 0x01ee -#define REG_G_ENABLE_PREV 0x01f0 -#define REG_G_ENABLE_PREV_CHG 0x01f2 -#define REG_G_NEW_CFG_SYNC 0x01f8 -#define REG_G_PREVZOOM_IN_WIDTH 0x020a -#define REG_G_PREVZOOM_IN_HEIGHT 0x020c -#define REG_G_PREVZOOM_IN_XOFFS 0x020e -#define REG_G_PREVZOOM_IN_YOFFS 0x0210 -#define REG_G_INPUTS_CHANGE_REQ 0x021a -#define REG_G_ACTIVE_PREV_CFG 0x021c -#define REG_G_PREV_CFG_CHG 0x021e -#define REG_G_PREV_OPEN_AFTER_CH 0x0220 -#define REG_G_PREV_CFG_ERROR 0x0222 - -/* Preview control section. n = 0...4. */ -#define PREG(n, x) ((n) * 0x26 + x) -#define REG_P_OUT_WIDTH(n) PREG(n, 0x0242) -#define REG_P_OUT_HEIGHT(n) PREG(n, 0x0244) -#define REG_P_FMT(n) PREG(n, 0x0246) -#define REG_P_MAX_OUT_RATE(n) PREG(n, 0x0248) -#define REG_P_MIN_OUT_RATE(n) PREG(n, 0x024a) -#define REG_P_PVI_MASK(n) PREG(n, 0x024c) -#define REG_P_CLK_INDEX(n) PREG(n, 0x024e) -#define REG_P_FR_RATE_TYPE(n) PREG(n, 0x0250) -#define FR_RATE_DYNAMIC 0 -#define FR_RATE_FIXED 1 -#define FR_RATE_FIXED_ACCURATE 2 -#define REG_P_FR_RATE_Q_TYPE(n) PREG(n, 0x0252) -#define FR_RATE_Q_BEST_FRRATE 1 /* Binning enabled */ -#define FR_RATE_Q_BEST_QUALITY 2 /* Binning disabled */ -/* Frame period in 0.1 ms units */ -#define REG_P_MAX_FR_TIME(n) PREG(n, 0x0254) -#define REG_P_MIN_FR_TIME(n) PREG(n, 0x0256) -/* Conversion to REG_P_[MAX/MIN]_FR_TIME value; __t: time in us */ -#define US_TO_FR_TIME(__t) ((__t) / 100) -#define S5K6AA_MIN_FR_TIME 33300 /* us */ -#define S5K6AA_MAX_FR_TIME 650000 /* us */ -#define S5K6AA_MAX_HIGHRES_FR_TIME 666 /* x100 us */ -/* The below 5 registers are for "device correction" values */ -#define REG_P_COLORTEMP(n) PREG(n, 0x025e) -#define REG_P_PREV_MIRROR(n) PREG(n, 0x0262) - -/* Extended image property controls */ -/* Exposure time in 10 us units */ -#define REG_SF_USR_EXPOSURE_L 0x03c6 -#define REG_SF_USR_EXPOSURE_H 0x03c8 -#define REG_SF_USR_EXPOSURE_CHG 0x03ca -#define REG_SF_USR_TOT_GAIN 0x03cc -#define REG_SF_USR_TOT_GAIN_CHG 0x03ce -#define REG_SF_RGAIN 0x03d0 -#define REG_SF_RGAIN_CHG 0x03d2 -#define REG_SF_GGAIN 0x03d4 -#define REG_SF_GGAIN_CHG 0x03d6 -#define REG_SF_BGAIN 0x03d8 -#define REG_SF_BGAIN_CHG 0x03da -#define REG_SF_FLICKER_QUANT 0x03dc -#define REG_SF_FLICKER_QUANT_CHG 0x03de - -/* Output interface (parallel/MIPI) setup */ -#define REG_OIF_EN_MIPI_LANES 0x03fa -#define REG_OIF_EN_PACKETS 0x03fc -#define REG_OIF_CFG_CHG 0x03fe - -/* Auto-algorithms enable mask */ -#define REG_DBG_AUTOALG_EN 0x0400 -#define AALG_ALL_EN_MASK (1 << 0) -#define AALG_AE_EN_MASK (1 << 1) -#define AALG_DIVLEI_EN_MASK (1 << 2) -#define AALG_WB_EN_MASK (1 << 3) -#define AALG_FLICKER_EN_MASK (1 << 5) -#define AALG_FIT_EN_MASK (1 << 6) -#define AALG_WRHW_EN_MASK (1 << 7) - -/* Firmware revision information */ -#define REG_FW_APIVER 0x012e -#define S5K6AAFX_FW_APIVER 0x0001 -#define REG_FW_REVISION 0x0130 - -/* For now we use only one user configuration register set */ -#define S5K6AA_MAX_PRESETS 1 - -static const char * const s5k6aa_supply_names[] = { - "vdd_core", /* Digital core supply 1.5V (1.4V to 1.6V) */ - "vdda", /* Analog power supply 2.8V (2.6V to 3.0V) */ - "vdd_reg", /* Regulator input power 1.8V (1.7V to 1.9V) - or 2.8V (2.6V to 3.0) */ - "vddio", /* I/O supply 1.8V (1.65V to 1.95V) - or 2.8V (2.5V to 3.1V) */ -}; -#define S5K6AA_NUM_SUPPLIES ARRAY_SIZE(s5k6aa_supply_names) - -enum s5k6aa_gpio_id { - STBY, - RST, - GPIO_NUM, -}; - -struct s5k6aa_regval { - u16 addr; - u16 val; -}; - -struct s5k6aa_pixfmt { - enum v4l2_mbus_pixelcode code; - u32 colorspace; - /* REG_P_FMT(x) register value */ - u16 reg_p_fmt; -}; - -struct s5k6aa_preset { - /* output pixel format and resolution */ - struct v4l2_mbus_framefmt mbus_fmt; - u8 clk_id; - u8 index; -}; - -struct s5k6aa_ctrls { - struct v4l2_ctrl_handler handler; - /* Auto / manual white balance cluster */ - struct v4l2_ctrl *awb; - struct v4l2_ctrl *gain_red; - struct v4l2_ctrl *gain_blue; - struct v4l2_ctrl *gain_green; - /* Mirror cluster */ - struct v4l2_ctrl *hflip; - struct v4l2_ctrl *vflip; - /* Auto exposure / manual exposure and gain cluster */ - struct v4l2_ctrl *auto_exp; - struct v4l2_ctrl *exposure; - struct v4l2_ctrl *gain; -}; - -struct s5k6aa_interval { - u16 reg_fr_time; - struct v4l2_fract interval; - /* Maximum rectangle for the interval */ - struct v4l2_frmsize_discrete size; -}; - -struct s5k6aa { - struct v4l2_subdev sd; - struct media_pad pad; - - enum v4l2_mbus_type bus_type; - u8 mipi_lanes; - - int (*s_power)(int enable); - struct regulator_bulk_data supplies[S5K6AA_NUM_SUPPLIES]; - struct s5k6aa_gpio gpio[GPIO_NUM]; - - /* external master clock frequency */ - unsigned long mclk_frequency; - /* ISP internal master clock frequency */ - u16 clk_fop; - /* output pixel clock frequency range */ - u16 pclk_fmin; - u16 pclk_fmax; - - unsigned int inv_hflip:1; - unsigned int inv_vflip:1; - - /* protects the struct members below */ - struct mutex lock; - - /* sensor matrix scan window */ - struct v4l2_rect ccd_rect; - - struct s5k6aa_ctrls ctrls; - struct s5k6aa_preset presets[S5K6AA_MAX_PRESETS]; - struct s5k6aa_preset *preset; - const struct s5k6aa_interval *fiv; - - unsigned int streaming:1; - unsigned int apply_cfg:1; - unsigned int apply_crop:1; - unsigned int power; -}; - -static struct s5k6aa_regval s5k6aa_analog_config[] = { - /* Analog settings */ - { 0x112a, 0x0000 }, { 0x1132, 0x0000 }, - { 0x113e, 0x0000 }, { 0x115c, 0x0000 }, - { 0x1164, 0x0000 }, { 0x1174, 0x0000 }, - { 0x1178, 0x0000 }, { 0x077a, 0x0000 }, - { 0x077c, 0x0000 }, { 0x077e, 0x0000 }, - { 0x0780, 0x0000 }, { 0x0782, 0x0000 }, - { 0x0784, 0x0000 }, { 0x0786, 0x0000 }, - { 0x0788, 0x0000 }, { 0x07a2, 0x0000 }, - { 0x07a4, 0x0000 }, { 0x07a6, 0x0000 }, - { 0x07a8, 0x0000 }, { 0x07b6, 0x0000 }, - { 0x07b8, 0x0002 }, { 0x07ba, 0x0004 }, - { 0x07bc, 0x0004 }, { 0x07be, 0x0005 }, - { 0x07c0, 0x0005 }, { S5K6AA_TERM, 0 }, -}; - -/* TODO: Add RGB888 and Bayer format */ -static const struct s5k6aa_pixfmt s5k6aa_formats[] = { - { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG, 5 }, - /* range 16-240 */ - { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_REC709, 6 }, - { V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_JPEG, 0 }, -}; - -static const struct s5k6aa_interval s5k6aa_intervals[] = { - { 1000, {10000, 1000000}, {1280, 1024} }, /* 10 fps */ - { 666, {15000, 1000000}, {1280, 1024} }, /* 15 fps */ - { 500, {20000, 1000000}, {1280, 720} }, /* 20 fps */ - { 400, {25000, 1000000}, {640, 480} }, /* 25 fps */ - { 333, {33300, 1000000}, {640, 480} }, /* 30 fps */ -}; - -#define S5K6AA_INTERVAL_DEF_INDEX 1 /* 15 fps */ - -static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct s5k6aa, ctrls.handler)->sd; -} - -static inline struct s5k6aa *to_s5k6aa(struct v4l2_subdev *sd) -{ - return container_of(sd, struct s5k6aa, sd); -} - -/* Set initial values for all preview presets */ -static void s5k6aa_presets_data_init(struct s5k6aa *s5k6aa) -{ - struct s5k6aa_preset *preset = &s5k6aa->presets[0]; - int i; - - for (i = 0; i < S5K6AA_MAX_PRESETS; i++) { - preset->mbus_fmt.width = S5K6AA_OUT_WIDTH_DEF; - preset->mbus_fmt.height = S5K6AA_OUT_HEIGHT_DEF; - preset->mbus_fmt.code = s5k6aa_formats[0].code; - preset->index = i; - preset->clk_id = 0; - preset++; - } - - s5k6aa->fiv = &s5k6aa_intervals[S5K6AA_INTERVAL_DEF_INDEX]; - s5k6aa->preset = &s5k6aa->presets[0]; -} - -static int s5k6aa_i2c_read(struct i2c_client *client, u16 addr, u16 *val) -{ - u8 wbuf[2] = {addr >> 8, addr & 0xFF}; - struct i2c_msg msg[2]; - u8 rbuf[2]; - int ret; - - msg[0].addr = client->addr; - msg[0].flags = 0; - msg[0].len = 2; - msg[0].buf = wbuf; - - msg[1].addr = client->addr; - msg[1].flags = I2C_M_RD; - msg[1].len = 2; - msg[1].buf = rbuf; - - ret = i2c_transfer(client->adapter, msg, 2); - *val = be16_to_cpu(*((u16 *)rbuf)); - - v4l2_dbg(3, debug, client, "i2c_read: 0x%04X : 0x%04x\n", addr, *val); - - return ret == 2 ? 0 : ret; -} - -static int s5k6aa_i2c_write(struct i2c_client *client, u16 addr, u16 val) -{ - u8 buf[4] = {addr >> 8, addr & 0xFF, val >> 8, val & 0xFF}; - - int ret = i2c_master_send(client, buf, 4); - v4l2_dbg(3, debug, client, "i2c_write: 0x%04X : 0x%04x\n", addr, val); - - return ret == 4 ? 0 : ret; -} - -/* The command register write, assumes Command_Wr_addH = 0x7000. */ -static int s5k6aa_write(struct i2c_client *c, u16 addr, u16 val) -{ - int ret = s5k6aa_i2c_write(c, REG_CMDWR_ADDRL, addr); - if (ret) - return ret; - return s5k6aa_i2c_write(c, REG_CMDBUF0_ADDR, val); -} - -/* The command register read, assumes Command_Rd_addH = 0x7000. */ -static int s5k6aa_read(struct i2c_client *client, u16 addr, u16 *val) -{ - int ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRL, addr); - if (ret) - return ret; - return s5k6aa_i2c_read(client, REG_CMDBUF0_ADDR, val); -} - -static int s5k6aa_write_array(struct v4l2_subdev *sd, - const struct s5k6aa_regval *msg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u16 addr_incr = 0; - int ret = 0; - - while (msg->addr != S5K6AA_TERM) { - if (addr_incr != 2) - ret = s5k6aa_i2c_write(client, REG_CMDWR_ADDRL, - msg->addr); - if (ret) - break; - ret = s5k6aa_i2c_write(client, REG_CMDBUF0_ADDR, msg->val); - if (ret) - break; - /* Assume that msg->addr is always less than 0xfffc */ - addr_incr = (msg + 1)->addr - msg->addr; - msg++; - } - - return ret; -} - -/* Configure the AHB high address bytes for GTG registers access */ -static int s5k6aa_set_ahb_address(struct i2c_client *client) -{ - int ret = s5k6aa_i2c_write(client, AHB_MSB_ADDR_PTR, GEN_REG_OFFSH); - if (ret) - return ret; - ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRH, HOST_SWIF_OFFSH); - if (ret) - return ret; - return s5k6aa_i2c_write(client, REG_CMDWR_ADDRH, HOST_SWIF_OFFSH); -} - -/** - * s5k6aa_configure_pixel_clock - apply ISP main clock/PLL configuration - * - * Configure the internal ISP PLL for the required output frequency. - * Locking: called with s5k6aa.lock mutex held. - */ -static int s5k6aa_configure_pixel_clocks(struct s5k6aa *s5k6aa) -{ - struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); - unsigned long fmclk = s5k6aa->mclk_frequency / 1000; - u16 status; - int ret; - - if (WARN(fmclk < MIN_MCLK_FREQ_KHZ || fmclk > MAX_MCLK_FREQ_KHZ, - "Invalid clock frequency: %ld\n", fmclk)) - return -EINVAL; - - s5k6aa->pclk_fmin = PCLK_FREQ_MIN; - s5k6aa->pclk_fmax = PCLK_FREQ_MAX; - s5k6aa->clk_fop = SYS_PLL_OUT_FREQ; - - /* External input clock frequency in kHz */ - ret = s5k6aa_write(c, REG_I_INCLK_FREQ_H, fmclk >> 16); - if (!ret) - ret = s5k6aa_write(c, REG_I_INCLK_FREQ_L, fmclk & 0xFFFF); - if (!ret) - ret = s5k6aa_write(c, REG_I_USE_NPVI_CLOCKS, 1); - /* Internal PLL frequency */ - if (!ret) - ret = s5k6aa_write(c, REG_I_OPCLK_4KHZ(0), s5k6aa->clk_fop); - if (!ret) - ret = s5k6aa_write(c, REG_I_MIN_OUTRATE_4KHZ(0), - s5k6aa->pclk_fmin); - if (!ret) - ret = s5k6aa_write(c, REG_I_MAX_OUTRATE_4KHZ(0), - s5k6aa->pclk_fmax); - if (!ret) - ret = s5k6aa_write(c, REG_I_INIT_PARAMS_UPDATED, 1); - if (!ret) - ret = s5k6aa_read(c, REG_I_ERROR_INFO, &status); - - return ret ? ret : (status ? -EINVAL : 0); -} - -/* Set horizontal and vertical image flipping */ -static int s5k6aa_set_mirror(struct s5k6aa *s5k6aa, int horiz_flip) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); - int index = s5k6aa->preset->index; - - unsigned int vflip = s5k6aa->ctrls.vflip->val ^ s5k6aa->inv_vflip; - unsigned int flip = (horiz_flip ^ s5k6aa->inv_hflip) | (vflip << 1); - - return s5k6aa_write(client, REG_P_PREV_MIRROR(index), flip); -} - -/* Configure auto/manual white balance and R/G/B gains */ -static int s5k6aa_set_awb(struct s5k6aa *s5k6aa, int awb) -{ - struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); - struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls; - u16 reg; - - int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, ®); - - if (!ret && !awb) { - ret = s5k6aa_write(c, REG_SF_RGAIN, ctrls->gain_red->val); - if (!ret) - ret = s5k6aa_write(c, REG_SF_RGAIN_CHG, 1); - if (ret) - return ret; - - ret = s5k6aa_write(c, REG_SF_GGAIN, ctrls->gain_green->val); - if (!ret) - ret = s5k6aa_write(c, REG_SF_GGAIN_CHG, 1); - if (ret) - return ret; - - ret = s5k6aa_write(c, REG_SF_BGAIN, ctrls->gain_blue->val); - if (!ret) - ret = s5k6aa_write(c, REG_SF_BGAIN_CHG, 1); - } - if (!ret) { - reg = awb ? reg | AALG_WB_EN_MASK : reg & ~AALG_WB_EN_MASK; - ret = s5k6aa_write(c, REG_DBG_AUTOALG_EN, reg); - } - - return ret; -} - -/* Program FW with exposure time, 'exposure' in us units */ -static int s5k6aa_set_user_exposure(struct i2c_client *client, int exposure) -{ - unsigned int time = exposure / 10; - - int ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_L, time & 0xffff); - if (!ret) - ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_H, time >> 16); - if (ret) - return ret; - return s5k6aa_write(client, REG_SF_USR_EXPOSURE_CHG, 1); -} - -static int s5k6aa_set_user_gain(struct i2c_client *client, int gain) -{ - int ret = s5k6aa_write(client, REG_SF_USR_TOT_GAIN, gain); - if (ret) - return ret; - return s5k6aa_write(client, REG_SF_USR_TOT_GAIN_CHG, 1); -} - -/* Set auto/manual exposure and total gain */ -static int s5k6aa_set_auto_exposure(struct s5k6aa *s5k6aa, int value) -{ - struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); - unsigned int exp_time = s5k6aa->ctrls.exposure->val; - u16 auto_alg; - - int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, &auto_alg); - if (ret) - return ret; - - v4l2_dbg(1, debug, c, "man_exp: %d, auto_exp: %d, a_alg: 0x%x\n", - exp_time, value, auto_alg); - - if (value == V4L2_EXPOSURE_AUTO) { - auto_alg |= AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK; - } else { - ret = s5k6aa_set_user_exposure(c, exp_time); - if (ret) - return ret; - ret = s5k6aa_set_user_gain(c, s5k6aa->ctrls.gain->val); - if (ret) - return ret; - auto_alg &= ~(AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK); - } - - return s5k6aa_write(c, REG_DBG_AUTOALG_EN, auto_alg); -} - -static int s5k6aa_set_anti_flicker(struct s5k6aa *s5k6aa, int value) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); - u16 auto_alg; - int ret; - - ret = s5k6aa_read(client, REG_DBG_AUTOALG_EN, &auto_alg); - if (ret) - return ret; - - if (value == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) { - auto_alg |= AALG_FLICKER_EN_MASK; - } else { - auto_alg &= ~AALG_FLICKER_EN_MASK; - /* The V4L2_CID_LINE_FREQUENCY control values match - * the register values */ - ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT, value); - if (ret) - return ret; - ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT_CHG, 1); - if (ret) - return ret; - } - - return s5k6aa_write(client, REG_DBG_AUTOALG_EN, auto_alg); -} - -static int s5k6aa_set_colorfx(struct s5k6aa *s5k6aa, int val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); - static const struct v4l2_control colorfx[] = { - { V4L2_COLORFX_NONE, 0 }, - { V4L2_COLORFX_BW, 1 }, - { V4L2_COLORFX_NEGATIVE, 2 }, - { V4L2_COLORFX_SEPIA, 3 }, - { V4L2_COLORFX_SKY_BLUE, 4 }, - { V4L2_COLORFX_SKETCH, 5 }, - }; - int i; - - for (i = 0; i < ARRAY_SIZE(colorfx); i++) { - if (colorfx[i].id == val) - return s5k6aa_write(client, REG_G_SPEC_EFFECTS, - colorfx[i].value); - } - return -EINVAL; -} - -static int s5k6aa_preview_config_status(struct i2c_client *client) -{ - u16 error = 0; - int ret = s5k6aa_read(client, REG_G_PREV_CFG_ERROR, &error); - - v4l2_dbg(1, debug, client, "error: 0x%x (%d)\n", error, ret); - return ret ? ret : (error ? -EINVAL : 0); -} - -static int s5k6aa_get_pixfmt_index(struct s5k6aa *s5k6aa, - struct v4l2_mbus_framefmt *mf) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(s5k6aa_formats); i++) - if (mf->colorspace == s5k6aa_formats[i].colorspace && - mf->code == s5k6aa_formats[i].code) - return i; - return 0; -} - -static int s5k6aa_set_output_framefmt(struct s5k6aa *s5k6aa, - struct s5k6aa_preset *preset) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); - int fmt_index = s5k6aa_get_pixfmt_index(s5k6aa, &preset->mbus_fmt); - int ret; - - ret = s5k6aa_write(client, REG_P_OUT_WIDTH(preset->index), - preset->mbus_fmt.width); - if (!ret) - ret = s5k6aa_write(client, REG_P_OUT_HEIGHT(preset->index), - preset->mbus_fmt.height); - if (!ret) - ret = s5k6aa_write(client, REG_P_FMT(preset->index), - s5k6aa_formats[fmt_index].reg_p_fmt); - return ret; -} - -static int s5k6aa_set_input_params(struct s5k6aa *s5k6aa) -{ - struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); - struct v4l2_rect *r = &s5k6aa->ccd_rect; - int ret; - - ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_WIDTH, r->width); - if (!ret) - ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_HEIGHT, r->height); - if (!ret) - ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_XOFFS, r->left); - if (!ret) - ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_YOFFS, r->top); - if (!ret) - ret = s5k6aa_write(c, REG_G_INPUTS_CHANGE_REQ, 1); - if (!ret) - s5k6aa->apply_crop = 0; - - return ret; -} - -/** - * s5k6aa_configure_video_bus - configure the video output interface - * @bus_type: video bus type: parallel or MIPI-CSI - * @nlanes: number of MIPI lanes to be used (MIPI-CSI only) - * - * Note: Only parallel bus operation has been tested. - */ -static int s5k6aa_configure_video_bus(struct s5k6aa *s5k6aa, - enum v4l2_mbus_type bus_type, int nlanes) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); - u16 cfg = 0; - int ret; - - /* - * TODO: The sensor is supposed to support BT.601 and BT.656 - * but there is nothing indicating how to switch between both - * in the datasheet. For now default BT.601 interface is assumed. - */ - if (bus_type == V4L2_MBUS_CSI2) - cfg = nlanes; - else if (bus_type != V4L2_MBUS_PARALLEL) - return -EINVAL; - - ret = s5k6aa_write(client, REG_OIF_EN_MIPI_LANES, cfg); - if (ret) - return ret; - return s5k6aa_write(client, REG_OIF_CFG_CHG, 1); -} - -/* This function should be called when switching to new user configuration set*/ -static int s5k6aa_new_config_sync(struct i2c_client *client, int timeout, - int cid) -{ - unsigned long end = jiffies + msecs_to_jiffies(timeout); - u16 reg = 1; - int ret; - - ret = s5k6aa_write(client, REG_G_ACTIVE_PREV_CFG, cid); - if (!ret) - ret = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1); - if (!ret) - ret = s5k6aa_write(client, REG_G_NEW_CFG_SYNC, 1); - if (timeout == 0) - return ret; - - while (ret >= 0 && time_is_after_jiffies(end)) { - ret = s5k6aa_read(client, REG_G_NEW_CFG_SYNC, ®); - if (!reg) - return 0; - usleep_range(1000, 5000); - } - return ret ? ret : -ETIMEDOUT; -} - -/** - * s5k6aa_set_prev_config - write user preview register set - * - * Configure output resolution and color fromat, pixel clock - * frequency range, device frame rate type and frame period range. - */ -static int s5k6aa_set_prev_config(struct s5k6aa *s5k6aa, - struct s5k6aa_preset *preset) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); - int idx = preset->index; - u16 frame_rate_q; - int ret; - - if (s5k6aa->fiv->reg_fr_time >= S5K6AA_MAX_HIGHRES_FR_TIME) - frame_rate_q = FR_RATE_Q_BEST_FRRATE; - else - frame_rate_q = FR_RATE_Q_BEST_QUALITY; - - ret = s5k6aa_set_output_framefmt(s5k6aa, preset); - if (!ret) - ret = s5k6aa_write(client, REG_P_MAX_OUT_RATE(idx), - s5k6aa->pclk_fmax); - if (!ret) - ret = s5k6aa_write(client, REG_P_MIN_OUT_RATE(idx), - s5k6aa->pclk_fmin); - if (!ret) - ret = s5k6aa_write(client, REG_P_CLK_INDEX(idx), - preset->clk_id); - if (!ret) - ret = s5k6aa_write(client, REG_P_FR_RATE_TYPE(idx), - FR_RATE_DYNAMIC); - if (!ret) - ret = s5k6aa_write(client, REG_P_FR_RATE_Q_TYPE(idx), - frame_rate_q); - if (!ret) - ret = s5k6aa_write(client, REG_P_MAX_FR_TIME(idx), - s5k6aa->fiv->reg_fr_time + 33); - if (!ret) - ret = s5k6aa_write(client, REG_P_MIN_FR_TIME(idx), - s5k6aa->fiv->reg_fr_time - 33); - if (!ret) - ret = s5k6aa_new_config_sync(client, 250, idx); - if (!ret) - ret = s5k6aa_preview_config_status(client); - if (!ret) - s5k6aa->apply_cfg = 0; - - v4l2_dbg(1, debug, client, "Frame interval: %d +/- 3.3ms. (%d)\n", - s5k6aa->fiv->reg_fr_time, ret); - return ret; -} - -/** - * s5k6aa_initialize_isp - basic ISP MCU initialization - * - * Configure AHB addresses for registers read/write; configure PLLs for - * required output pixel clock. The ISP power supply needs to be already - * enabled, with an optional H/W reset. - * Locking: called with s5k6aa.lock mutex held. - */ -static int s5k6aa_initialize_isp(struct v4l2_subdev *sd) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - int ret; - - s5k6aa->apply_crop = 1; - s5k6aa->apply_cfg = 1; - msleep(100); - - ret = s5k6aa_set_ahb_address(client); - if (ret) - return ret; - ret = s5k6aa_configure_video_bus(s5k6aa, s5k6aa->bus_type, - s5k6aa->mipi_lanes); - if (ret) - return ret; - ret = s5k6aa_write_array(sd, s5k6aa_analog_config); - if (ret) - return ret; - msleep(20); - - return s5k6aa_configure_pixel_clocks(s5k6aa); -} - -static int s5k6aa_gpio_set_value(struct s5k6aa *priv, int id, u32 val) -{ - if (!gpio_is_valid(priv->gpio[id].gpio)) - return 0; - gpio_set_value(priv->gpio[id].gpio, !!val); - return 1; -} - -static int s5k6aa_gpio_assert(struct s5k6aa *priv, int id) -{ - return s5k6aa_gpio_set_value(priv, id, priv->gpio[id].level); -} - -static int s5k6aa_gpio_deassert(struct s5k6aa *priv, int id) -{ - return s5k6aa_gpio_set_value(priv, id, !priv->gpio[id].level); -} - -static int __s5k6aa_power_on(struct s5k6aa *s5k6aa) -{ - int ret; - - ret = regulator_bulk_enable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); - if (ret) - return ret; - if (s5k6aa_gpio_deassert(s5k6aa, STBY)) - usleep_range(150, 200); - - if (s5k6aa->s_power) - ret = s5k6aa->s_power(1); - usleep_range(4000, 4000); - - if (s5k6aa_gpio_deassert(s5k6aa, RST)) - msleep(20); - - return ret; -} - -static int __s5k6aa_power_off(struct s5k6aa *s5k6aa) -{ - int ret; - - if (s5k6aa_gpio_assert(s5k6aa, RST)) - usleep_range(100, 150); - - if (s5k6aa->s_power) { - ret = s5k6aa->s_power(0); - if (ret) - return ret; - } - if (s5k6aa_gpio_assert(s5k6aa, STBY)) - usleep_range(50, 100); - s5k6aa->streaming = 0; - - return regulator_bulk_disable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); -} - -/* - * V4L2 subdev core and video operations - */ -static int s5k6aa_set_power(struct v4l2_subdev *sd, int on) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - int ret = 0; - - mutex_lock(&s5k6aa->lock); - - if (!on == s5k6aa->power) { - if (on) { - ret = __s5k6aa_power_on(s5k6aa); - if (!ret) - ret = s5k6aa_initialize_isp(sd); - } else { - ret = __s5k6aa_power_off(s5k6aa); - } - - if (!ret) - s5k6aa->power += on ? 1 : -1; - } - - mutex_unlock(&s5k6aa->lock); - - if (!on || ret || s5k6aa->power != 1) - return ret; - - return v4l2_ctrl_handler_setup(sd->ctrl_handler); -} - -static int __s5k6aa_stream(struct s5k6aa *s5k6aa, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); - int ret = 0; - - ret = s5k6aa_write(client, REG_G_ENABLE_PREV, enable); - if (!ret) - ret = s5k6aa_write(client, REG_G_ENABLE_PREV_CHG, 1); - if (!ret) - s5k6aa->streaming = enable; - - return ret; -} - -static int s5k6aa_s_stream(struct v4l2_subdev *sd, int on) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - int ret = 0; - - mutex_lock(&s5k6aa->lock); - - if (s5k6aa->streaming == !on) { - if (!ret && s5k6aa->apply_cfg) - ret = s5k6aa_set_prev_config(s5k6aa, s5k6aa->preset); - if (s5k6aa->apply_crop) - ret = s5k6aa_set_input_params(s5k6aa); - if (!ret) - ret = __s5k6aa_stream(s5k6aa, !!on); - } - mutex_unlock(&s5k6aa->lock); - - return ret; -} - -static int s5k6aa_g_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *fi) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - - mutex_lock(&s5k6aa->lock); - fi->interval = s5k6aa->fiv->interval; - mutex_unlock(&s5k6aa->lock); - - return 0; -} - -static int __s5k6aa_set_frame_interval(struct s5k6aa *s5k6aa, - struct v4l2_subdev_frame_interval *fi) -{ - struct v4l2_mbus_framefmt *mbus_fmt = &s5k6aa->preset->mbus_fmt; - const struct s5k6aa_interval *fiv = &s5k6aa_intervals[0]; - unsigned int err, min_err = UINT_MAX; - unsigned int i, fr_time; - - if (fi->interval.denominator == 0) - return -EINVAL; - - fr_time = fi->interval.numerator * 10000 / fi->interval.denominator; - - for (i = 0; i < ARRAY_SIZE(s5k6aa_intervals); i++) { - const struct s5k6aa_interval *iv = &s5k6aa_intervals[i]; - - if (mbus_fmt->width > iv->size.width || - mbus_fmt->height > iv->size.height) - continue; - - err = abs(iv->reg_fr_time - fr_time); - if (err < min_err) { - fiv = iv; - min_err = err; - } - } - s5k6aa->fiv = fiv; - - v4l2_dbg(1, debug, &s5k6aa->sd, "Changed frame interval to %d us\n", - fiv->reg_fr_time * 100); - return 0; -} - -static int s5k6aa_s_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *fi) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - int ret; - - v4l2_dbg(1, debug, sd, "Setting %d/%d frame interval\n", - fi->interval.numerator, fi->interval.denominator); - - mutex_lock(&s5k6aa->lock); - ret = __s5k6aa_set_frame_interval(s5k6aa, fi); - s5k6aa->apply_cfg = 1; - - mutex_unlock(&s5k6aa->lock); - return ret; -} - -/* - * V4L2 subdev pad level and video operations - */ -static int s5k6aa_enum_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_frame_interval_enum *fie) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - const struct s5k6aa_interval *fi; - int ret = 0; - - if (fie->index > ARRAY_SIZE(s5k6aa_intervals)) - return -EINVAL; - - v4l_bound_align_image(&fie->width, S5K6AA_WIN_WIDTH_MIN, - S5K6AA_WIN_WIDTH_MAX, 1, - &fie->height, S5K6AA_WIN_HEIGHT_MIN, - S5K6AA_WIN_HEIGHT_MAX, 1, 0); - - mutex_lock(&s5k6aa->lock); - fi = &s5k6aa_intervals[fie->index]; - if (fie->width > fi->size.width || fie->height > fi->size.height) - ret = -EINVAL; - else - fie->interval = fi->interval; - mutex_unlock(&s5k6aa->lock); - - return ret; -} - -static int s5k6aa_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->index >= ARRAY_SIZE(s5k6aa_formats)) - return -EINVAL; - - code->code = s5k6aa_formats[code->index].code; - return 0; -} - -static int s5k6aa_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_frame_size_enum *fse) -{ - int i = ARRAY_SIZE(s5k6aa_formats); - - if (fse->index > 0) - return -EINVAL; - - while (--i) - if (fse->code == s5k6aa_formats[i].code) - break; - - fse->code = s5k6aa_formats[i].code; - fse->min_width = S5K6AA_WIN_WIDTH_MIN; - fse->max_width = S5K6AA_WIN_WIDTH_MAX; - fse->max_height = S5K6AA_WIN_HEIGHT_MIN; - fse->min_height = S5K6AA_WIN_HEIGHT_MAX; - - return 0; -} - -static struct v4l2_rect * -__s5k6aa_get_crop_rect(struct s5k6aa *s5k6aa, struct v4l2_subdev_fh *fh, - enum v4l2_subdev_format_whence which) -{ - if (which == V4L2_SUBDEV_FORMAT_ACTIVE) - return &s5k6aa->ccd_rect; - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(fh, 0); - - return NULL; -} - -static void s5k6aa_try_format(struct s5k6aa *s5k6aa, - struct v4l2_mbus_framefmt *mf) -{ - unsigned int index; - - v4l_bound_align_image(&mf->width, S5K6AA_WIN_WIDTH_MIN, - S5K6AA_WIN_WIDTH_MAX, 1, - &mf->height, S5K6AA_WIN_HEIGHT_MIN, - S5K6AA_WIN_HEIGHT_MAX, 1, 0); - - if (mf->colorspace != V4L2_COLORSPACE_JPEG && - mf->colorspace != V4L2_COLORSPACE_REC709) - mf->colorspace = V4L2_COLORSPACE_JPEG; - - index = s5k6aa_get_pixfmt_index(s5k6aa, mf); - - mf->colorspace = s5k6aa_formats[index].colorspace; - mf->code = s5k6aa_formats[index].code; - mf->field = V4L2_FIELD_NONE; -} - -static int s5k6aa_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *fmt) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - struct v4l2_mbus_framefmt *mf; - - memset(fmt->reserved, 0, sizeof(fmt->reserved)); - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(fh, 0); - fmt->format = *mf; - return 0; - } - - mutex_lock(&s5k6aa->lock); - fmt->format = s5k6aa->preset->mbus_fmt; - mutex_unlock(&s5k6aa->lock); - - return 0; -} - -static int s5k6aa_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *fmt) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - struct s5k6aa_preset *preset = s5k6aa->preset; - struct v4l2_mbus_framefmt *mf; - struct v4l2_rect *crop; - int ret = 0; - - mutex_lock(&s5k6aa->lock); - s5k6aa_try_format(s5k6aa, &fmt->format); - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(fh, fmt->pad); - crop = v4l2_subdev_get_try_crop(fh, 0); - } else { - if (s5k6aa->streaming) { - ret = -EBUSY; - } else { - mf = &preset->mbus_fmt; - crop = &s5k6aa->ccd_rect; - s5k6aa->apply_cfg = 1; - } - } - - if (ret == 0) { - struct v4l2_subdev_frame_interval fiv = { - .interval = {0, 1} - }; - - *mf = fmt->format; - /* - * Make sure the crop window is valid, i.e. its size is - * greater than the output window, as the ISP supports - * only down-scaling. - */ - crop->width = clamp_t(unsigned int, crop->width, mf->width, - S5K6AA_WIN_WIDTH_MAX); - crop->height = clamp_t(unsigned int, crop->height, mf->height, - S5K6AA_WIN_HEIGHT_MAX); - crop->left = clamp_t(unsigned int, crop->left, 0, - S5K6AA_WIN_WIDTH_MAX - crop->width); - crop->top = clamp_t(unsigned int, crop->top, 0, - S5K6AA_WIN_HEIGHT_MAX - crop->height); - - /* Reset to minimum possible frame interval */ - ret = __s5k6aa_set_frame_interval(s5k6aa, &fiv); - } - mutex_unlock(&s5k6aa->lock); - - return ret; -} - -static int s5k6aa_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - struct v4l2_rect *rect; - - memset(crop->reserved, 0, sizeof(crop->reserved)); - mutex_lock(&s5k6aa->lock); - - rect = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which); - if (rect) - crop->rect = *rect; - - mutex_unlock(&s5k6aa->lock); - - v4l2_dbg(1, debug, sd, "Current crop rectangle: (%d,%d)/%dx%d\n", - rect->left, rect->top, rect->width, rect->height); - - return 0; -} - -static int s5k6aa_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - struct v4l2_mbus_framefmt *mf; - unsigned int max_x, max_y; - struct v4l2_rect *crop_r; - - mutex_lock(&s5k6aa->lock); - crop_r = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which); - - if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - mf = &s5k6aa->preset->mbus_fmt; - s5k6aa->apply_crop = 1; - } else { - mf = v4l2_subdev_get_try_format(fh, 0); - } - v4l_bound_align_image(&crop->rect.width, mf->width, - S5K6AA_WIN_WIDTH_MAX, 1, - &crop->rect.height, mf->height, - S5K6AA_WIN_HEIGHT_MAX, 1, 0); - - max_x = (S5K6AA_WIN_WIDTH_MAX - crop->rect.width) & ~1; - max_y = (S5K6AA_WIN_HEIGHT_MAX - crop->rect.height) & ~1; - - crop->rect.left = clamp_t(unsigned int, crop->rect.left, 0, max_x); - crop->rect.top = clamp_t(unsigned int, crop->rect.top, 0, max_y); - - *crop_r = crop->rect; - - mutex_unlock(&s5k6aa->lock); - - v4l2_dbg(1, debug, sd, "Set crop rectangle: (%d,%d)/%dx%d\n", - crop_r->left, crop_r->top, crop_r->width, crop_r->height); - - return 0; -} - -static const struct v4l2_subdev_pad_ops s5k6aa_pad_ops = { - .enum_mbus_code = s5k6aa_enum_mbus_code, - .enum_frame_size = s5k6aa_enum_frame_size, - .enum_frame_interval = s5k6aa_enum_frame_interval, - .get_fmt = s5k6aa_get_fmt, - .set_fmt = s5k6aa_set_fmt, - .get_crop = s5k6aa_get_crop, - .set_crop = s5k6aa_set_crop, -}; - -static const struct v4l2_subdev_video_ops s5k6aa_video_ops = { - .g_frame_interval = s5k6aa_g_frame_interval, - .s_frame_interval = s5k6aa_s_frame_interval, - .s_stream = s5k6aa_s_stream, -}; - -/* - * V4L2 subdev controls - */ - -static int s5k6aa_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = ctrl_to_sd(ctrl); - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - int idx, err = 0; - - v4l2_dbg(1, debug, sd, "ctrl: 0x%x, value: %d\n", ctrl->id, ctrl->val); - - mutex_lock(&s5k6aa->lock); - /* - * If the device is not powered up by the host driver do - * not apply any controls to H/W at this time. Instead - * the controls will be restored right after power-up. - */ - if (s5k6aa->power == 0) - goto unlock; - idx = s5k6aa->preset->index; - - switch (ctrl->id) { - case V4L2_CID_AUTO_WHITE_BALANCE: - err = s5k6aa_set_awb(s5k6aa, ctrl->val); - break; - - case V4L2_CID_BRIGHTNESS: - err = s5k6aa_write(client, REG_USER_BRIGHTNESS, ctrl->val); - break; - - case V4L2_CID_COLORFX: - err = s5k6aa_set_colorfx(s5k6aa, ctrl->val); - break; - - case V4L2_CID_CONTRAST: - err = s5k6aa_write(client, REG_USER_CONTRAST, ctrl->val); - break; - - case V4L2_CID_EXPOSURE_AUTO: - err = s5k6aa_set_auto_exposure(s5k6aa, ctrl->val); - break; - - case V4L2_CID_HFLIP: - err = s5k6aa_set_mirror(s5k6aa, ctrl->val); - if (err) - break; - err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1); - break; - - case V4L2_CID_POWER_LINE_FREQUENCY: - err = s5k6aa_set_anti_flicker(s5k6aa, ctrl->val); - break; - - case V4L2_CID_SATURATION: - err = s5k6aa_write(client, REG_USER_SATURATION, ctrl->val); - break; - - case V4L2_CID_SHARPNESS: - err = s5k6aa_write(client, REG_USER_SHARPBLUR, ctrl->val); - break; - - case V4L2_CID_WHITE_BALANCE_TEMPERATURE: - err = s5k6aa_write(client, REG_P_COLORTEMP(idx), ctrl->val); - if (err) - break; - err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1); - break; - } -unlock: - mutex_unlock(&s5k6aa->lock); - return err; -} - -static const struct v4l2_ctrl_ops s5k6aa_ctrl_ops = { - .s_ctrl = s5k6aa_s_ctrl, -}; - -static int s5k6aa_log_status(struct v4l2_subdev *sd) -{ - v4l2_ctrl_handler_log_status(sd->ctrl_handler, sd->name); - return 0; -} - -#define V4L2_CID_RED_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1001) -#define V4L2_CID_GREEN_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1002) -#define V4L2_CID_BLUE_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1003) - -static const struct v4l2_ctrl_config s5k6aa_ctrls[] = { - { - .ops = &s5k6aa_ctrl_ops, - .id = V4L2_CID_RED_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain, Red", - .min = 0, - .max = 256, - .def = 127, - .step = 1, - }, { - .ops = &s5k6aa_ctrl_ops, - .id = V4L2_CID_GREEN_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain, Green", - .min = 0, - .max = 256, - .def = 127, - .step = 1, - }, { - .ops = &s5k6aa_ctrl_ops, - .id = V4L2_CID_BLUE_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain, Blue", - .min = 0, - .max = 256, - .def = 127, - .step = 1, - }, -}; - -static int s5k6aa_initialize_ctrls(struct s5k6aa *s5k6aa) -{ - const struct v4l2_ctrl_ops *ops = &s5k6aa_ctrl_ops; - struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls; - struct v4l2_ctrl_handler *hdl = &ctrls->handler; - - int ret = v4l2_ctrl_handler_init(hdl, 16); - if (ret) - return ret; - /* Auto white balance cluster */ - ctrls->awb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, - 0, 1, 1, 1); - ctrls->gain_red = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[0], NULL); - ctrls->gain_green = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[1], NULL); - ctrls->gain_blue = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[2], NULL); - v4l2_ctrl_auto_cluster(4, &ctrls->awb, 0, false); - - ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); - ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_cluster(2, &ctrls->hflip); - - ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, - V4L2_CID_EXPOSURE_AUTO, - V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO); - /* Exposure time: x 1 us */ - ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, - 0, 6000000U, 1, 100000U); - /* Total gain: 256 <=> 1x */ - ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, - 0, 256, 1, 256); - v4l2_ctrl_auto_cluster(3, &ctrls->auto_exp, 0, false); - - v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY, - V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, - V4L2_CID_POWER_LINE_FREQUENCY_AUTO); - - v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX, - V4L2_COLORFX_SKY_BLUE, ~0x6f, V4L2_COLORFX_NONE); - - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_WHITE_BALANCE_TEMPERATURE, - 0, 256, 1, 0); - - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, -127, 127, 1, 0); - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -127, 127, 1, 0); - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -127, 127, 1, 0); - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, -127, 127, 1, 0); - - if (hdl->error) { - ret = hdl->error; - v4l2_ctrl_handler_free(hdl); - return ret; - } - - s5k6aa->sd.ctrl_handler = hdl; - return 0; -} - -/* - * V4L2 subdev internal operations - */ -static int s5k6aa_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); - struct v4l2_rect *crop = v4l2_subdev_get_try_crop(fh, 0); - - format->colorspace = s5k6aa_formats[0].colorspace; - format->code = s5k6aa_formats[0].code; - format->width = S5K6AA_OUT_WIDTH_DEF; - format->height = S5K6AA_OUT_HEIGHT_DEF; - format->field = V4L2_FIELD_NONE; - - crop->width = S5K6AA_WIN_WIDTH_MAX; - crop->height = S5K6AA_WIN_HEIGHT_MAX; - crop->left = 0; - crop->top = 0; - - return 0; -} - -static int s5k6aa_check_fw_revision(struct s5k6aa *s5k6aa) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); - u16 api_ver = 0, fw_rev = 0; - - int ret = s5k6aa_set_ahb_address(client); - - if (!ret) - ret = s5k6aa_read(client, REG_FW_APIVER, &api_ver); - if (!ret) - ret = s5k6aa_read(client, REG_FW_REVISION, &fw_rev); - if (ret) { - v4l2_err(&s5k6aa->sd, "FW revision check failed!\n"); - return ret; - } - - v4l2_info(&s5k6aa->sd, "FW API ver.: 0x%X, FW rev.: 0x%X\n", - api_ver, fw_rev); - - return api_ver == S5K6AAFX_FW_APIVER ? 0 : -ENODEV; -} - -static int s5k6aa_registered(struct v4l2_subdev *sd) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - int ret; - - mutex_lock(&s5k6aa->lock); - ret = __s5k6aa_power_on(s5k6aa); - if (!ret) { - msleep(100); - ret = s5k6aa_check_fw_revision(s5k6aa); - __s5k6aa_power_off(s5k6aa); - } - mutex_unlock(&s5k6aa->lock); - - return ret; -} - -static const struct v4l2_subdev_internal_ops s5k6aa_subdev_internal_ops = { - .registered = s5k6aa_registered, - .open = s5k6aa_open, -}; - -static const struct v4l2_subdev_core_ops s5k6aa_core_ops = { - .s_power = s5k6aa_set_power, - .log_status = s5k6aa_log_status, -}; - -static const struct v4l2_subdev_ops s5k6aa_subdev_ops = { - .core = &s5k6aa_core_ops, - .pad = &s5k6aa_pad_ops, - .video = &s5k6aa_video_ops, -}; - -/* - * GPIO setup - */ -static int s5k6aa_configure_gpio(int nr, int val, const char *name) -{ - unsigned long flags = val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; - int ret; - - if (!gpio_is_valid(nr)) - return 0; - ret = gpio_request_one(nr, flags, name); - if (!ret) - gpio_export(nr, 0); - return ret; -} - -static void s5k6aa_free_gpios(struct s5k6aa *s5k6aa) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(s5k6aa->gpio); i++) { - if (!gpio_is_valid(s5k6aa->gpio[i].gpio)) - continue; - gpio_free(s5k6aa->gpio[i].gpio); - s5k6aa->gpio[i].gpio = -EINVAL; - } -} - -static int s5k6aa_configure_gpios(struct s5k6aa *s5k6aa, - const struct s5k6aa_platform_data *pdata) -{ - const struct s5k6aa_gpio *gpio = &pdata->gpio_stby; - int ret; - - s5k6aa->gpio[STBY].gpio = -EINVAL; - s5k6aa->gpio[RST].gpio = -EINVAL; - - ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_STBY"); - if (ret) { - s5k6aa_free_gpios(s5k6aa); - return ret; - } - s5k6aa->gpio[STBY] = *gpio; - if (gpio_is_valid(gpio->gpio)) - gpio_set_value(gpio->gpio, 0); - - gpio = &pdata->gpio_reset; - ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_RST"); - if (ret) { - s5k6aa_free_gpios(s5k6aa); - return ret; - } - s5k6aa->gpio[RST] = *gpio; - if (gpio_is_valid(gpio->gpio)) - gpio_set_value(gpio->gpio, 0); - - return 0; -} - -static int s5k6aa_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - const struct s5k6aa_platform_data *pdata = client->dev.platform_data; - struct v4l2_subdev *sd; - struct s5k6aa *s5k6aa; - int i, ret; - - if (pdata == NULL) { - dev_err(&client->dev, "Platform data not specified\n"); - return -EINVAL; - } - - if (pdata->mclk_frequency == 0) { - dev_err(&client->dev, "MCLK frequency not specified\n"); - return -EINVAL; - } - - s5k6aa = devm_kzalloc(&client->dev, sizeof(*s5k6aa), GFP_KERNEL); - if (!s5k6aa) - return -ENOMEM; - - mutex_init(&s5k6aa->lock); - - s5k6aa->mclk_frequency = pdata->mclk_frequency; - s5k6aa->bus_type = pdata->bus_type; - s5k6aa->mipi_lanes = pdata->nlanes; - s5k6aa->s_power = pdata->set_power; - s5k6aa->inv_hflip = pdata->horiz_flip; - s5k6aa->inv_vflip = pdata->vert_flip; - - sd = &s5k6aa->sd; - v4l2_i2c_subdev_init(sd, client, &s5k6aa_subdev_ops); - strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name)); - - sd->internal_ops = &s5k6aa_subdev_internal_ops; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - s5k6aa->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - ret = media_entity_init(&sd->entity, 1, &s5k6aa->pad, 0); - if (ret) - return ret; - - ret = s5k6aa_configure_gpios(s5k6aa, pdata); - if (ret) - goto out_err2; - - for (i = 0; i < S5K6AA_NUM_SUPPLIES; i++) - s5k6aa->supplies[i].supply = s5k6aa_supply_names[i]; - - ret = regulator_bulk_get(&client->dev, S5K6AA_NUM_SUPPLIES, - s5k6aa->supplies); - if (ret) { - dev_err(&client->dev, "Failed to get regulators\n"); - goto out_err3; - } - - ret = s5k6aa_initialize_ctrls(s5k6aa); - if (ret) - goto out_err4; - - s5k6aa_presets_data_init(s5k6aa); - - s5k6aa->ccd_rect.width = S5K6AA_WIN_WIDTH_MAX; - s5k6aa->ccd_rect.height = S5K6AA_WIN_HEIGHT_MAX; - s5k6aa->ccd_rect.left = 0; - s5k6aa->ccd_rect.top = 0; - - return 0; - -out_err4: - regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); -out_err3: - s5k6aa_free_gpios(s5k6aa); -out_err2: - media_entity_cleanup(&s5k6aa->sd.entity); - return ret; -} - -static int s5k6aa_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(sd->ctrl_handler); - media_entity_cleanup(&sd->entity); - regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); - s5k6aa_free_gpios(s5k6aa); - - return 0; -} - -static const struct i2c_device_id s5k6aa_id[] = { - { DRIVER_NAME, 0 }, - { }, -}; -MODULE_DEVICE_TABLE(i2c, s5k6aa_id); - - -static struct i2c_driver s5k6aa_i2c_driver = { - .driver = { - .name = DRIVER_NAME - }, - .probe = s5k6aa_probe, - .remove = s5k6aa_remove, - .id_table = s5k6aa_id, -}; - -module_i2c_driver(s5k6aa_i2c_driver); - -MODULE_DESCRIPTION("Samsung S5K6AA(FX) SXGA camera driver"); -MODULE_AUTHOR("Sylwester Nawrocki "); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/saa6588.c b/drivers/media/video/saa6588.c deleted file mode 100644 index 0caac50d7cf..00000000000 --- a/drivers/media/video/saa6588.c +++ /dev/null @@ -1,542 +0,0 @@ -/* - Driver for SAA6588 RDS decoder - - (c) 2005 Hans J. Koch - - 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - - -/* insmod options */ -static unsigned int debug; -static unsigned int xtal; -static unsigned int mmbs; -static unsigned int plvl; -static unsigned int bufblocks = 100; - -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "enable debug messages"); -module_param(xtal, int, 0); -MODULE_PARM_DESC(xtal, "select oscillator frequency (0..3), default 0"); -module_param(mmbs, int, 0); -MODULE_PARM_DESC(mmbs, "enable MMBS mode: 0=off (default), 1=on"); -module_param(plvl, int, 0); -MODULE_PARM_DESC(plvl, "select pause level (0..3), default 0"); -module_param(bufblocks, int, 0); -MODULE_PARM_DESC(bufblocks, "number of buffered blocks, default 100"); - -MODULE_DESCRIPTION("v4l2 driver module for SAA6588 RDS decoder"); -MODULE_AUTHOR("Hans J. Koch "); - -MODULE_LICENSE("GPL"); - -/* ---------------------------------------------------------------------- */ - -#define UNSET (-1U) -#define PREFIX "saa6588: " -#define dprintk if (debug) printk - -struct saa6588 { - struct v4l2_subdev sd; - struct delayed_work work; - spinlock_t lock; - unsigned char *buffer; - unsigned int buf_size; - unsigned int rd_index; - unsigned int wr_index; - unsigned int block_count; - unsigned char last_blocknum; - wait_queue_head_t read_queue; - int data_available_for_read; - u8 sync; -}; - -static inline struct saa6588 *to_saa6588(struct v4l2_subdev *sd) -{ - return container_of(sd, struct saa6588, sd); -} - -/* ---------------------------------------------------------------------- */ - -/* - * SAA6588 defines - */ - -/* Initialization and mode control byte (0w) */ - -/* bit 0+1 (DAC0/DAC1) */ -#define cModeStandard 0x00 -#define cModeFastPI 0x01 -#define cModeReducedRequest 0x02 -#define cModeInvalid 0x03 - -/* bit 2 (RBDS) */ -#define cProcessingModeRDS 0x00 -#define cProcessingModeRBDS 0x04 - -/* bit 3+4 (SYM0/SYM1) */ -#define cErrCorrectionNone 0x00 -#define cErrCorrection2Bits 0x08 -#define cErrCorrection5Bits 0x10 -#define cErrCorrectionNoneRBDS 0x18 - -/* bit 5 (NWSY) */ -#define cSyncNormal 0x00 -#define cSyncRestart 0x20 - -/* bit 6 (TSQD) */ -#define cSigQualityDetectOFF 0x00 -#define cSigQualityDetectON 0x40 - -/* bit 7 (SQCM) */ -#define cSigQualityTriggered 0x00 -#define cSigQualityContinous 0x80 - -/* Pause level and flywheel control byte (1w) */ - -/* bits 0..5 (FEB0..FEB5) */ -#define cFlywheelMaxBlocksMask 0x3F -#define cFlywheelDefault 0x20 - -/* bits 6+7 (PL0/PL1) */ -#define cPauseLevel_11mV 0x00 -#define cPauseLevel_17mV 0x40 -#define cPauseLevel_27mV 0x80 -#define cPauseLevel_43mV 0xC0 - -/* Pause time/oscillator frequency/quality detector control byte (1w) */ - -/* bits 0..4 (SQS0..SQS4) */ -#define cQualityDetectSensMask 0x1F -#define cQualityDetectDefault 0x0F - -/* bit 5 (SOSC) */ -#define cSelectOscFreqOFF 0x00 -#define cSelectOscFreqON 0x20 - -/* bit 6+7 (PTF0/PTF1) */ -#define cOscFreq_4332kHz 0x00 -#define cOscFreq_8664kHz 0x40 -#define cOscFreq_12996kHz 0x80 -#define cOscFreq_17328kHz 0xC0 - -/* ---------------------------------------------------------------------- */ - -static int block_to_user_buf(struct saa6588 *s, unsigned char __user *user_buf) -{ - int i; - - if (s->rd_index == s->wr_index) { - if (debug > 2) - dprintk(PREFIX "Read: buffer empty.\n"); - return 0; - } - - if (debug > 2) { - dprintk(PREFIX "Read: "); - for (i = s->rd_index; i < s->rd_index + 3; i++) - dprintk("0x%02x ", s->buffer[i]); - } - - if (copy_to_user(user_buf, &s->buffer[s->rd_index], 3)) - return -EFAULT; - - s->rd_index += 3; - if (s->rd_index >= s->buf_size) - s->rd_index = 0; - s->block_count--; - - if (debug > 2) - dprintk("%d blocks total.\n", s->block_count); - - return 1; -} - -static void read_from_buf(struct saa6588 *s, struct saa6588_command *a) -{ - unsigned long flags; - - unsigned char __user *buf_ptr = a->buffer; - unsigned int i; - unsigned int rd_blocks; - - a->result = 0; - if (!a->buffer) - return; - - while (!s->data_available_for_read) { - int ret = wait_event_interruptible(s->read_queue, - s->data_available_for_read); - if (ret == -ERESTARTSYS) { - a->result = -EINTR; - return; - } - } - - spin_lock_irqsave(&s->lock, flags); - rd_blocks = a->block_count; - if (rd_blocks > s->block_count) - rd_blocks = s->block_count; - - if (!rd_blocks) { - spin_unlock_irqrestore(&s->lock, flags); - return; - } - - for (i = 0; i < rd_blocks; i++) { - if (block_to_user_buf(s, buf_ptr)) { - buf_ptr += 3; - a->result++; - } else - break; - } - a->result *= 3; - s->data_available_for_read = (s->block_count > 0); - spin_unlock_irqrestore(&s->lock, flags); -} - -static void block_to_buf(struct saa6588 *s, unsigned char *blockbuf) -{ - unsigned int i; - - if (debug > 3) - dprintk(PREFIX "New block: "); - - for (i = 0; i < 3; ++i) { - if (debug > 3) - dprintk("0x%02x ", blockbuf[i]); - s->buffer[s->wr_index] = blockbuf[i]; - s->wr_index++; - } - - if (s->wr_index >= s->buf_size) - s->wr_index = 0; - - if (s->wr_index == s->rd_index) { - s->rd_index += 3; - if (s->rd_index >= s->buf_size) - s->rd_index = 0; - } else - s->block_count++; - - if (debug > 3) - dprintk("%d blocks total.\n", s->block_count); -} - -static void saa6588_i2c_poll(struct saa6588 *s) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s->sd); - unsigned long flags; - unsigned char tmpbuf[6]; - unsigned char blocknum; - unsigned char tmp; - - /* Although we only need 3 bytes, we have to read at least 6. - SAA6588 returns garbage otherwise. */ - if (6 != i2c_master_recv(client, &tmpbuf[0], 6)) { - if (debug > 1) - dprintk(PREFIX "read error!\n"); - return; - } - - s->sync = tmpbuf[0] & 0x10; - if (!s->sync) - return; - blocknum = tmpbuf[0] >> 5; - if (blocknum == s->last_blocknum) { - if (debug > 3) - dprintk("Saw block %d again.\n", blocknum); - return; - } - - s->last_blocknum = blocknum; - - /* - Byte order according to v4l2 specification: - - Byte 0: Least Significant Byte of RDS Block - Byte 1: Most Significant Byte of RDS Block - Byte 2 Bit 7: Error bit. Indicates that an uncorrectable error - occurred during reception of this block. - Bit 6: Corrected bit. Indicates that an error was - corrected for this data block. - Bits 5-3: Same as bits 0-2. - Bits 2-0: Block number. - - SAA6588 byte order is Status-MSB-LSB, so we have to swap the - first and the last of the 3 bytes block. - */ - - tmp = tmpbuf[2]; - tmpbuf[2] = tmpbuf[0]; - tmpbuf[0] = tmp; - - /* Map 'Invalid block E' to 'Invalid Block' */ - if (blocknum == 6) - blocknum = V4L2_RDS_BLOCK_INVALID; - /* And if are not in mmbs mode, then 'Block E' is also mapped - to 'Invalid Block'. As far as I can tell MMBS is discontinued, - and if there is ever a need to support E blocks, then please - contact the linux-media mailinglist. */ - else if (!mmbs && blocknum == 5) - blocknum = V4L2_RDS_BLOCK_INVALID; - tmp = blocknum; - tmp |= blocknum << 3; /* Received offset == Offset Name (OK ?) */ - if ((tmpbuf[2] & 0x03) == 0x03) - tmp |= V4L2_RDS_BLOCK_ERROR; /* uncorrectable error */ - else if ((tmpbuf[2] & 0x03) != 0x00) - tmp |= V4L2_RDS_BLOCK_CORRECTED; /* corrected error */ - tmpbuf[2] = tmp; /* Is this enough ? Should we also check other bits ? */ - - spin_lock_irqsave(&s->lock, flags); - block_to_buf(s, tmpbuf); - spin_unlock_irqrestore(&s->lock, flags); - s->data_available_for_read = 1; - wake_up_interruptible(&s->read_queue); -} - -static void saa6588_work(struct work_struct *work) -{ - struct saa6588 *s = container_of(work, struct saa6588, work.work); - - saa6588_i2c_poll(s); - schedule_delayed_work(&s->work, msecs_to_jiffies(20)); -} - -static void saa6588_configure(struct saa6588 *s) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s->sd); - unsigned char buf[3]; - int rc; - - buf[0] = cSyncRestart; - if (mmbs) - buf[0] |= cProcessingModeRBDS; - - buf[1] = cFlywheelDefault; - switch (plvl) { - case 0: - buf[1] |= cPauseLevel_11mV; - break; - case 1: - buf[1] |= cPauseLevel_17mV; - break; - case 2: - buf[1] |= cPauseLevel_27mV; - break; - case 3: - buf[1] |= cPauseLevel_43mV; - break; - default: /* nothing */ - break; - } - - buf[2] = cQualityDetectDefault | cSelectOscFreqON; - - switch (xtal) { - case 0: - buf[2] |= cOscFreq_4332kHz; - break; - case 1: - buf[2] |= cOscFreq_8664kHz; - break; - case 2: - buf[2] |= cOscFreq_12996kHz; - break; - case 3: - buf[2] |= cOscFreq_17328kHz; - break; - default: /* nothing */ - break; - } - - dprintk(PREFIX "writing: 0w=0x%02x 1w=0x%02x 2w=0x%02x\n", - buf[0], buf[1], buf[2]); - - rc = i2c_master_send(client, buf, 3); - if (rc != 3) - printk(PREFIX "i2c i/o error: rc == %d (should be 3)\n", rc); -} - -/* ---------------------------------------------------------------------- */ - -static long saa6588_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) -{ - struct saa6588 *s = to_saa6588(sd); - struct saa6588_command *a = arg; - - switch (cmd) { - /* --- open() for /dev/radio --- */ - case SAA6588_CMD_OPEN: - a->result = 0; /* return error if chip doesn't work ??? */ - break; - /* --- close() for /dev/radio --- */ - case SAA6588_CMD_CLOSE: - s->data_available_for_read = 1; - wake_up_interruptible(&s->read_queue); - a->result = 0; - break; - /* --- read() for /dev/radio --- */ - case SAA6588_CMD_READ: - read_from_buf(s, a); - break; - /* --- poll() for /dev/radio --- */ - case SAA6588_CMD_POLL: - a->result = 0; - if (s->data_available_for_read) { - a->result |= POLLIN | POLLRDNORM; - } - poll_wait(a->instance, &s->read_queue, a->event_list); - break; - - default: - /* nothing */ - return -ENOIOCTLCMD; - } - return 0; -} - -static int saa6588_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct saa6588 *s = to_saa6588(sd); - - vt->capability |= V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO; - if (s->sync) - vt->rxsubchans |= V4L2_TUNER_SUB_RDS; - return 0; -} - -static int saa6588_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct saa6588 *s = to_saa6588(sd); - - saa6588_configure(s); - return 0; -} - -static int saa6588_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA6588, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops saa6588_core_ops = { - .g_chip_ident = saa6588_g_chip_ident, - .ioctl = saa6588_ioctl, -}; - -static const struct v4l2_subdev_tuner_ops saa6588_tuner_ops = { - .g_tuner = saa6588_g_tuner, - .s_tuner = saa6588_s_tuner, -}; - -static const struct v4l2_subdev_ops saa6588_ops = { - .core = &saa6588_core_ops, - .tuner = &saa6588_tuner_ops, -}; - -/* ---------------------------------------------------------------------- */ - -static int saa6588_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct saa6588 *s; - struct v4l2_subdev *sd; - - v4l_info(client, "saa6588 found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - s = kzalloc(sizeof(*s), GFP_KERNEL); - if (s == NULL) - return -ENOMEM; - - s->buf_size = bufblocks * 3; - - s->buffer = kmalloc(s->buf_size, GFP_KERNEL); - if (s->buffer == NULL) { - kfree(s); - return -ENOMEM; - } - sd = &s->sd; - v4l2_i2c_subdev_init(sd, client, &saa6588_ops); - spin_lock_init(&s->lock); - s->block_count = 0; - s->wr_index = 0; - s->rd_index = 0; - s->last_blocknum = 0xff; - init_waitqueue_head(&s->read_queue); - s->data_available_for_read = 0; - - saa6588_configure(s); - - /* start polling via eventd */ - INIT_DELAYED_WORK(&s->work, saa6588_work); - schedule_delayed_work(&s->work, 0); - return 0; -} - -static int saa6588_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct saa6588 *s = to_saa6588(sd); - - v4l2_device_unregister_subdev(sd); - - cancel_delayed_work_sync(&s->work); - - kfree(s->buffer); - kfree(s); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id saa6588_id[] = { - { "saa6588", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, saa6588_id); - -static struct i2c_driver saa6588_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "saa6588", - }, - .probe = saa6588_probe, - .remove = saa6588_remove, - .id_table = saa6588_id, -}; - -module_i2c_driver(saa6588_driver); diff --git a/drivers/media/video/saa7110.c b/drivers/media/video/saa7110.c deleted file mode 100644 index 51cd4c8f052..00000000000 --- a/drivers/media/video/saa7110.c +++ /dev/null @@ -1,494 +0,0 @@ -/* - * saa7110 - Philips SAA7110(A) video decoder driver - * - * Copyright (C) 1998 Pauline Middelink - * - * Copyright (C) 1999 Wolfgang Scherr - * Copyright (C) 2000 Serguei Miridonov - * - some corrections for Pinnacle Systems Inc. DC10plus card. - * - * Changes by Ronald Bultje - * - moved over to linux>=2.4.x i2c protocol (1/1/2003) - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("Philips SAA7110 video decoder driver"); -MODULE_AUTHOR("Pauline Middelink"); -MODULE_LICENSE("GPL"); - - -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - -#define SAA7110_MAX_INPUT 9 /* 6 CVBS, 3 SVHS */ -#define SAA7110_MAX_OUTPUT 1 /* 1 YUV */ - -#define SAA7110_NR_REG 0x35 - -struct saa7110 { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - u8 reg[SAA7110_NR_REG]; - - v4l2_std_id norm; - int input; - int enable; - - wait_queue_head_t wq; -}; - -static inline struct saa7110 *to_saa7110(struct v4l2_subdev *sd) -{ - return container_of(sd, struct saa7110, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct saa7110, hdl)->sd; -} - -/* ----------------------------------------------------------------------- */ -/* I2C support functions */ -/* ----------------------------------------------------------------------- */ - -static int saa7110_write(struct v4l2_subdev *sd, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct saa7110 *decoder = to_saa7110(sd); - - decoder->reg[reg] = value; - return i2c_smbus_write_byte_data(client, reg, value); -} - -static int saa7110_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct saa7110 *decoder = to_saa7110(sd); - int ret = -1; - u8 reg = *data; /* first register to write to */ - - /* Sanity check */ - if (reg + (len - 1) > SAA7110_NR_REG) - return ret; - - /* the saa7110 has an autoincrement function, use it if - * the adapter understands raw I2C */ - if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - ret = i2c_master_send(client, data, len); - - /* Cache the written data */ - memcpy(decoder->reg + reg, data + 1, len - 1); - } else { - for (++data, --len; len; len--) { - ret = saa7110_write(sd, reg++, *data++); - if (ret < 0) - break; - } - } - - return ret; -} - -static inline int saa7110_read(struct v4l2_subdev *sd) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_read_byte(client); -} - -/* ----------------------------------------------------------------------- */ -/* SAA7110 functions */ -/* ----------------------------------------------------------------------- */ - -#define FRESP_06H_COMPST 0x03 /*0x13*/ -#define FRESP_06H_SVIDEO 0x83 /*0xC0*/ - - -static int saa7110_selmux(struct v4l2_subdev *sd, int chan) -{ - static const unsigned char modes[9][8] = { - /* mode 0 */ - {FRESP_06H_COMPST, 0xD9, 0x17, 0x40, 0x03, - 0x44, 0x75, 0x16}, - /* mode 1 */ - {FRESP_06H_COMPST, 0xD8, 0x17, 0x40, 0x03, - 0x44, 0x75, 0x16}, - /* mode 2 */ - {FRESP_06H_COMPST, 0xBA, 0x07, 0x91, 0x03, - 0x60, 0xB5, 0x05}, - /* mode 3 */ - {FRESP_06H_COMPST, 0xB8, 0x07, 0x91, 0x03, - 0x60, 0xB5, 0x05}, - /* mode 4 */ - {FRESP_06H_COMPST, 0x7C, 0x07, 0xD2, 0x83, - 0x60, 0xB5, 0x03}, - /* mode 5 */ - {FRESP_06H_COMPST, 0x78, 0x07, 0xD2, 0x83, - 0x60, 0xB5, 0x03}, - /* mode 6 */ - {FRESP_06H_SVIDEO, 0x59, 0x17, 0x42, 0xA3, - 0x44, 0x75, 0x12}, - /* mode 7 */ - {FRESP_06H_SVIDEO, 0x9A, 0x17, 0xB1, 0x13, - 0x60, 0xB5, 0x14}, - /* mode 8 */ - {FRESP_06H_SVIDEO, 0x3C, 0x27, 0xC1, 0x23, - 0x44, 0x75, 0x21} - }; - struct saa7110 *decoder = to_saa7110(sd); - const unsigned char *ptr = modes[chan]; - - saa7110_write(sd, 0x06, ptr[0]); /* Luminance control */ - saa7110_write(sd, 0x20, ptr[1]); /* Analog Control #1 */ - saa7110_write(sd, 0x21, ptr[2]); /* Analog Control #2 */ - saa7110_write(sd, 0x22, ptr[3]); /* Mixer Control #1 */ - saa7110_write(sd, 0x2C, ptr[4]); /* Mixer Control #2 */ - saa7110_write(sd, 0x30, ptr[5]); /* ADCs gain control */ - saa7110_write(sd, 0x31, ptr[6]); /* Mixer Control #3 */ - saa7110_write(sd, 0x21, ptr[7]); /* Analog Control #2 */ - decoder->input = chan; - - return 0; -} - -static const unsigned char initseq[1 + SAA7110_NR_REG] = { - 0, 0x4C, 0x3C, 0x0D, 0xEF, 0xBD, 0xF2, 0x03, 0x00, - /* 0x08 */ 0xF8, 0xF8, 0x60, 0x60, 0x00, 0x86, 0x18, 0x90, - /* 0x10 */ 0x00, 0x59, 0x40, 0x46, 0x42, 0x1A, 0xFF, 0xDA, - /* 0x18 */ 0xF2, 0x8B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 0x20 */ 0xD9, 0x16, 0x40, 0x41, 0x80, 0x41, 0x80, 0x4F, - /* 0x28 */ 0xFE, 0x01, 0xCF, 0x0F, 0x03, 0x01, 0x03, 0x0C, - /* 0x30 */ 0x44, 0x71, 0x02, 0x8C, 0x02 -}; - -static v4l2_std_id determine_norm(struct v4l2_subdev *sd) -{ - DEFINE_WAIT(wait); - struct saa7110 *decoder = to_saa7110(sd); - int status; - - /* mode changed, start automatic detection */ - saa7110_write_block(sd, initseq, sizeof(initseq)); - saa7110_selmux(sd, decoder->input); - prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE); - schedule_timeout(msecs_to_jiffies(250)); - finish_wait(&decoder->wq, &wait); - status = saa7110_read(sd); - if (status & 0x40) { - v4l2_dbg(1, debug, sd, "status=0x%02x (no signal)\n", status); - return decoder->norm; /* no change*/ - } - if ((status & 3) == 0) { - saa7110_write(sd, 0x06, 0x83); - if (status & 0x20) { - v4l2_dbg(1, debug, sd, "status=0x%02x (NTSC/no color)\n", status); - /*saa7110_write(sd,0x2E,0x81);*/ - return V4L2_STD_NTSC; - } - v4l2_dbg(1, debug, sd, "status=0x%02x (PAL/no color)\n", status); - /*saa7110_write(sd,0x2E,0x9A);*/ - return V4L2_STD_PAL; - } - /*saa7110_write(sd,0x06,0x03);*/ - if (status & 0x20) { /* 60Hz */ - v4l2_dbg(1, debug, sd, "status=0x%02x (NTSC)\n", status); - saa7110_write(sd, 0x0D, 0x86); - saa7110_write(sd, 0x0F, 0x50); - saa7110_write(sd, 0x11, 0x2C); - /*saa7110_write(sd,0x2E,0x81);*/ - return V4L2_STD_NTSC; - } - - /* 50Hz -> PAL/SECAM */ - saa7110_write(sd, 0x0D, 0x86); - saa7110_write(sd, 0x0F, 0x10); - saa7110_write(sd, 0x11, 0x59); - /*saa7110_write(sd,0x2E,0x9A);*/ - - prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE); - schedule_timeout(msecs_to_jiffies(250)); - finish_wait(&decoder->wq, &wait); - - status = saa7110_read(sd); - if ((status & 0x03) == 0x01) { - v4l2_dbg(1, debug, sd, "status=0x%02x (SECAM)\n", status); - saa7110_write(sd, 0x0D, 0x87); - return V4L2_STD_SECAM; - } - v4l2_dbg(1, debug, sd, "status=0x%02x (PAL)\n", status); - return V4L2_STD_PAL; -} - -static int saa7110_g_input_status(struct v4l2_subdev *sd, u32 *pstatus) -{ - struct saa7110 *decoder = to_saa7110(sd); - int res = V4L2_IN_ST_NO_SIGNAL; - int status = saa7110_read(sd); - - v4l2_dbg(1, debug, sd, "status=0x%02x norm=%llx\n", - status, (unsigned long long)decoder->norm); - if (!(status & 0x40)) - res = 0; - if (!(status & 0x03)) - res |= V4L2_IN_ST_NO_COLOR; - - *pstatus = res; - return 0; -} - -static int saa7110_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) -{ - *(v4l2_std_id *)std = determine_norm(sd); - return 0; -} - -static int saa7110_s_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct saa7110 *decoder = to_saa7110(sd); - - if (decoder->norm != std) { - decoder->norm = std; - /*saa7110_write(sd, 0x06, 0x03);*/ - if (std & V4L2_STD_NTSC) { - saa7110_write(sd, 0x0D, 0x86); - saa7110_write(sd, 0x0F, 0x50); - saa7110_write(sd, 0x11, 0x2C); - /*saa7110_write(sd, 0x2E, 0x81);*/ - v4l2_dbg(1, debug, sd, "switched to NTSC\n"); - } else if (std & V4L2_STD_PAL) { - saa7110_write(sd, 0x0D, 0x86); - saa7110_write(sd, 0x0F, 0x10); - saa7110_write(sd, 0x11, 0x59); - /*saa7110_write(sd, 0x2E, 0x9A);*/ - v4l2_dbg(1, debug, sd, "switched to PAL\n"); - } else if (std & V4L2_STD_SECAM) { - saa7110_write(sd, 0x0D, 0x87); - saa7110_write(sd, 0x0F, 0x10); - saa7110_write(sd, 0x11, 0x59); - /*saa7110_write(sd, 0x2E, 0x9A);*/ - v4l2_dbg(1, debug, sd, "switched to SECAM\n"); - } else { - return -EINVAL; - } - } - return 0; -} - -static int saa7110_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct saa7110 *decoder = to_saa7110(sd); - - if (input >= SAA7110_MAX_INPUT) { - v4l2_dbg(1, debug, sd, "input=%d not available\n", input); - return -EINVAL; - } - if (decoder->input != input) { - saa7110_selmux(sd, input); - v4l2_dbg(1, debug, sd, "switched to input=%d\n", input); - } - return 0; -} - -static int saa7110_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct saa7110 *decoder = to_saa7110(sd); - - if (decoder->enable != enable) { - decoder->enable = enable; - saa7110_write(sd, 0x0E, enable ? 0x18 : 0x80); - v4l2_dbg(1, debug, sd, "YUV %s\n", enable ? "on" : "off"); - } - return 0; -} - -static int saa7110_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - saa7110_write(sd, 0x19, ctrl->val); - break; - case V4L2_CID_CONTRAST: - saa7110_write(sd, 0x13, ctrl->val); - break; - case V4L2_CID_SATURATION: - saa7110_write(sd, 0x12, ctrl->val); - break; - case V4L2_CID_HUE: - saa7110_write(sd, 0x07, ctrl->val); - break; - default: - return -EINVAL; - } - return 0; -} - -static int saa7110_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7110, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_ctrl_ops saa7110_ctrl_ops = { - .s_ctrl = saa7110_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops saa7110_core_ops = { - .g_chip_ident = saa7110_g_chip_ident, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, - .s_std = saa7110_s_std, -}; - -static const struct v4l2_subdev_video_ops saa7110_video_ops = { - .s_routing = saa7110_s_routing, - .s_stream = saa7110_s_stream, - .querystd = saa7110_querystd, - .g_input_status = saa7110_g_input_status, -}; - -static const struct v4l2_subdev_ops saa7110_ops = { - .core = &saa7110_core_ops, - .video = &saa7110_video_ops, -}; - -/* ----------------------------------------------------------------------- */ - -static int saa7110_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct saa7110 *decoder; - struct v4l2_subdev *sd; - int rv; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) - return -ENODEV; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - decoder = kzalloc(sizeof(struct saa7110), GFP_KERNEL); - if (!decoder) - return -ENOMEM; - sd = &decoder->sd; - v4l2_i2c_subdev_init(sd, client, &saa7110_ops); - decoder->norm = V4L2_STD_PAL; - decoder->input = 0; - decoder->enable = 1; - v4l2_ctrl_handler_init(&decoder->hdl, 2); - v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); - v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, - V4L2_CID_CONTRAST, 0, 127, 1, 64); - v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, - V4L2_CID_SATURATION, 0, 127, 1, 64); - v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, - V4L2_CID_HUE, -128, 127, 1, 0); - sd->ctrl_handler = &decoder->hdl; - if (decoder->hdl.error) { - int err = decoder->hdl.error; - - v4l2_ctrl_handler_free(&decoder->hdl); - kfree(decoder); - return err; - } - v4l2_ctrl_handler_setup(&decoder->hdl); - - init_waitqueue_head(&decoder->wq); - - rv = saa7110_write_block(sd, initseq, sizeof(initseq)); - if (rv < 0) { - v4l2_dbg(1, debug, sd, "init status %d\n", rv); - } else { - int ver, status; - saa7110_write(sd, 0x21, 0x10); - saa7110_write(sd, 0x0e, 0x18); - saa7110_write(sd, 0x0D, 0x04); - ver = saa7110_read(sd); - saa7110_write(sd, 0x0D, 0x06); - /*mdelay(150);*/ - status = saa7110_read(sd); - v4l2_dbg(1, debug, sd, "version %x, status=0x%02x\n", - ver, status); - saa7110_write(sd, 0x0D, 0x86); - saa7110_write(sd, 0x0F, 0x10); - saa7110_write(sd, 0x11, 0x59); - /*saa7110_write(sd, 0x2E, 0x9A);*/ - } - - /*saa7110_selmux(sd,0);*/ - /*determine_norm(sd);*/ - /* setup and implicit mode 0 select has been performed */ - - return 0; -} - -static int saa7110_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct saa7110 *decoder = to_saa7110(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&decoder->hdl); - kfree(decoder); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id saa7110_id[] = { - { "saa7110", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, saa7110_id); - -static struct i2c_driver saa7110_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "saa7110", - }, - .probe = saa7110_probe, - .remove = saa7110_remove, - .id_table = saa7110_id, -}; - -module_i2c_driver(saa7110_driver); diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c deleted file mode 100644 index 2107336cd83..00000000000 --- a/drivers/media/video/saa7115.c +++ /dev/null @@ -1,1727 +0,0 @@ -/* saa711x - Philips SAA711x video decoder driver - * This driver can work with saa7111, saa7111a, saa7113, saa7114, - * saa7115 and saa7118. - * - * Based on saa7114 driver by Maxim Yevtyushkin, which is based on - * the saa7111 driver by Dave Perks. - * - * Copyright (C) 1998 Dave Perks - * Copyright (C) 2002 Maxim Yevtyushkin - * - * Slight changes for video timing and attachment output by - * Wolfgang Scherr - * - * Moved over to the linux >= 2.4.x i2c protocol (1/1/2003) - * by Ronald Bultje - * - * Added saa7115 support by Kevin Thayer - * (2/17/2003) - * - * VBI support (2004) and cleanups (2005) by Hans Verkuil - * - * Copyright (c) 2005-2006 Mauro Carvalho Chehab - * SAA7111, SAA7113 and SAA7118 support - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "saa711x_regs.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define VRES_60HZ (480+16) - -MODULE_DESCRIPTION("Philips SAA7111/SAA7113/SAA7114/SAA7115/SAA7118 video decoder driver"); -MODULE_AUTHOR( "Maxim Yevtyushkin, Kevin Thayer, Chris Kennedy, " - "Hans Verkuil, Mauro Carvalho Chehab"); -MODULE_LICENSE("GPL"); - -static bool debug; -module_param(debug, bool, 0644); - -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - - -struct saa711x_state { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - - struct { - /* chroma gain control cluster */ - struct v4l2_ctrl *agc; - struct v4l2_ctrl *gain; - }; - - v4l2_std_id std; - int input; - int output; - int enable; - int radio; - int width; - int height; - u32 ident; - u32 audclk_freq; - u32 crystal_freq; - u8 ucgc; - u8 cgcdiv; - u8 apll; -}; - -static inline struct saa711x_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct saa711x_state, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct saa711x_state, hdl)->sd; -} - -/* ----------------------------------------------------------------------- */ - -static inline int saa711x_write(struct v4l2_subdev *sd, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_write_byte_data(client, reg, value); -} - -/* Sanity routine to check if a register is present */ -static int saa711x_has_reg(const int id, const u8 reg) -{ - if (id == V4L2_IDENT_SAA7111) - return reg < 0x20 && reg != 0x01 && reg != 0x0f && - (reg < 0x13 || reg > 0x19) && reg != 0x1d && reg != 0x1e; - if (id == V4L2_IDENT_SAA7111A) - return reg < 0x20 && reg != 0x01 && reg != 0x0f && - reg != 0x14 && reg != 0x18 && reg != 0x19 && - reg != 0x1d && reg != 0x1e; - - /* common for saa7113/4/5/8 */ - if (unlikely((reg >= 0x3b && reg <= 0x3f) || reg == 0x5c || reg == 0x5f || - reg == 0xa3 || reg == 0xa7 || reg == 0xab || reg == 0xaf || (reg >= 0xb5 && reg <= 0xb7) || - reg == 0xd3 || reg == 0xd7 || reg == 0xdb || reg == 0xdf || (reg >= 0xe5 && reg <= 0xe7) || - reg == 0x82 || (reg >= 0x89 && reg <= 0x8e))) - return 0; - - switch (id) { - case V4L2_IDENT_SAA7113: - return reg != 0x14 && (reg < 0x18 || reg > 0x1e) && (reg < 0x20 || reg > 0x3f) && - reg != 0x5d && reg < 0x63; - case V4L2_IDENT_SAA7114: - return (reg < 0x1a || reg > 0x1e) && (reg < 0x20 || reg > 0x2f) && - (reg < 0x63 || reg > 0x7f) && reg != 0x33 && reg != 0x37 && - reg != 0x81 && reg < 0xf0; - case V4L2_IDENT_SAA7115: - return (reg < 0x20 || reg > 0x2f) && reg != 0x65 && (reg < 0xfc || reg > 0xfe); - case V4L2_IDENT_SAA7118: - return (reg < 0x1a || reg > 0x1d) && (reg < 0x20 || reg > 0x22) && - (reg < 0x26 || reg > 0x28) && reg != 0x33 && reg != 0x37 && - (reg < 0x63 || reg > 0x7f) && reg != 0x81 && reg < 0xf0; - } - return 1; -} - -static int saa711x_writeregs(struct v4l2_subdev *sd, const unsigned char *regs) -{ - struct saa711x_state *state = to_state(sd); - unsigned char reg, data; - - while (*regs != 0x00) { - reg = *(regs++); - data = *(regs++); - - /* According with datasheets, reserved regs should be - filled with 0 - seems better not to touch on they */ - if (saa711x_has_reg(state->ident, reg)) { - if (saa711x_write(sd, reg, data) < 0) - return -1; - } else { - v4l2_dbg(1, debug, sd, "tried to access reserved reg 0x%02x\n", reg); - } - } - return 0; -} - -static inline int saa711x_read(struct v4l2_subdev *sd, u8 reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_read_byte_data(client, reg); -} - -/* ----------------------------------------------------------------------- */ - -/* SAA7111 initialization table */ -static const unsigned char saa7111_init[] = { - R_01_INC_DELAY, 0x00, /* reserved */ - - /*front end */ - R_02_INPUT_CNTL_1, 0xd0, /* FUSE=3, GUDL=2, MODE=0 */ - R_03_INPUT_CNTL_2, 0x23, /* HLNRS=0, VBSL=1, WPOFF=0, HOLDG=0, - * GAFIX=0, GAI1=256, GAI2=256 */ - R_04_INPUT_CNTL_3, 0x00, /* GAI1=256 */ - R_05_INPUT_CNTL_4, 0x00, /* GAI2=256 */ - - /* decoder */ - R_06_H_SYNC_START, 0xf3, /* HSB at 13(50Hz) / 17(60Hz) - * pixels after end of last line */ - R_07_H_SYNC_STOP, 0xe8, /* HSS seems to be needed to - * work with NTSC, too */ - R_08_SYNC_CNTL, 0xc8, /* AUFD=1, FSEL=1, EXFIL=0, - * VTRC=1, HPLL=0, VNOI=0 */ - R_09_LUMA_CNTL, 0x01, /* BYPS=0, PREF=0, BPSS=0, - * VBLB=0, UPTCV=0, APER=1 */ - R_0A_LUMA_BRIGHT_CNTL, 0x80, - R_0B_LUMA_CONTRAST_CNTL, 0x47, /* 0b - CONT=1.109 */ - R_0C_CHROMA_SAT_CNTL, 0x40, - R_0D_CHROMA_HUE_CNTL, 0x00, - R_0E_CHROMA_CNTL_1, 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0, - * FCTC=0, CHBW=1 */ - R_0F_CHROMA_GAIN_CNTL, 0x00, /* reserved */ - R_10_CHROMA_CNTL_2, 0x48, /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */ - R_11_MODE_DELAY_CNTL, 0x1c, /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1, - * OEYC=1, OEHV=1, VIPB=0, COLO=0 */ - R_12_RT_SIGNAL_CNTL, 0x00, /* 12 - output control 2 */ - R_13_RT_X_PORT_OUT_CNTL, 0x00, /* 13 - output control 3 */ - R_14_ANAL_ADC_COMPAT_CNTL, 0x00, - R_15_VGATE_START_FID_CHG, 0x00, - R_16_VGATE_STOP, 0x00, - R_17_MISC_VGATE_CONF_AND_MSB, 0x00, - - 0x00, 0x00 -}; - -/* SAA7113 init codes */ -static const unsigned char saa7113_init[] = { - R_01_INC_DELAY, 0x08, - R_02_INPUT_CNTL_1, 0xc2, - R_03_INPUT_CNTL_2, 0x30, - R_04_INPUT_CNTL_3, 0x00, - R_05_INPUT_CNTL_4, 0x00, - R_06_H_SYNC_START, 0x89, - R_07_H_SYNC_STOP, 0x0d, - R_08_SYNC_CNTL, 0x88, - R_09_LUMA_CNTL, 0x01, - R_0A_LUMA_BRIGHT_CNTL, 0x80, - R_0B_LUMA_CONTRAST_CNTL, 0x47, - R_0C_CHROMA_SAT_CNTL, 0x40, - R_0D_CHROMA_HUE_CNTL, 0x00, - R_0E_CHROMA_CNTL_1, 0x01, - R_0F_CHROMA_GAIN_CNTL, 0x2a, - R_10_CHROMA_CNTL_2, 0x08, - R_11_MODE_DELAY_CNTL, 0x0c, - R_12_RT_SIGNAL_CNTL, 0x07, - R_13_RT_X_PORT_OUT_CNTL, 0x00, - R_14_ANAL_ADC_COMPAT_CNTL, 0x00, - R_15_VGATE_START_FID_CHG, 0x00, - R_16_VGATE_STOP, 0x00, - R_17_MISC_VGATE_CONF_AND_MSB, 0x00, - - 0x00, 0x00 -}; - -/* If a value differs from the Hauppauge driver values, then the comment starts with - 'was 0xXX' to denote the Hauppauge value. Otherwise the value is identical to what the - Hauppauge driver sets. */ - -/* SAA7114 and SAA7115 initialization table */ -static const unsigned char saa7115_init_auto_input[] = { - /* Front-End Part */ - R_01_INC_DELAY, 0x48, /* white peak control disabled */ - R_03_INPUT_CNTL_2, 0x20, /* was 0x30. 0x20: long vertical blanking */ - R_04_INPUT_CNTL_3, 0x90, /* analog gain set to 0 */ - R_05_INPUT_CNTL_4, 0x90, /* analog gain set to 0 */ - /* Decoder Part */ - R_06_H_SYNC_START, 0xeb, /* horiz sync begin = -21 */ - R_07_H_SYNC_STOP, 0xe0, /* horiz sync stop = -17 */ - R_09_LUMA_CNTL, 0x53, /* 0x53, was 0x56 for 60hz. luminance control */ - R_0A_LUMA_BRIGHT_CNTL, 0x80, /* was 0x88. decoder brightness, 0x80 is itu standard */ - R_0B_LUMA_CONTRAST_CNTL, 0x44, /* was 0x48. decoder contrast, 0x44 is itu standard */ - R_0C_CHROMA_SAT_CNTL, 0x40, /* was 0x47. decoder saturation, 0x40 is itu standard */ - R_0D_CHROMA_HUE_CNTL, 0x00, - R_0F_CHROMA_GAIN_CNTL, 0x00, /* use automatic gain */ - R_10_CHROMA_CNTL_2, 0x06, /* chroma: active adaptive combfilter */ - R_11_MODE_DELAY_CNTL, 0x00, - R_12_RT_SIGNAL_CNTL, 0x9d, /* RTS0 output control: VGATE */ - R_13_RT_X_PORT_OUT_CNTL, 0x80, /* ITU656 standard mode, RTCO output enable RTCE */ - R_14_ANAL_ADC_COMPAT_CNTL, 0x00, - R_18_RAW_DATA_GAIN_CNTL, 0x40, /* gain 0x00 = nominal */ - R_19_RAW_DATA_OFF_CNTL, 0x80, - R_1A_COLOR_KILL_LVL_CNTL, 0x77, /* recommended value */ - R_1B_MISC_TVVCRDET, 0x42, /* recommended value */ - R_1C_ENHAN_COMB_CTRL1, 0xa9, /* recommended value */ - R_1D_ENHAN_COMB_CTRL2, 0x01, /* recommended value */ - - - R_80_GLOBAL_CNTL_1, 0x0, /* No tasks enabled at init */ - - /* Power Device Control */ - R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset device */ - R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* set device programmed, all in operational mode */ - 0x00, 0x00 -}; - -/* Used to reset saa7113, saa7114 and saa7115 */ -static const unsigned char saa7115_cfg_reset_scaler[] = { - R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x00, /* disable I-port output */ - R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ - R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */ - R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* enable I-port output */ - 0x00, 0x00 -}; - -/* ============== SAA7715 VIDEO templates ============= */ - -static const unsigned char saa7115_cfg_60hz_video[] = { - R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */ - R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ - - R_15_VGATE_START_FID_CHG, 0x03, - R_16_VGATE_STOP, 0x11, - R_17_MISC_VGATE_CONF_AND_MSB, 0x9c, - - R_08_SYNC_CNTL, 0x68, /* 0xBO: auto detection, 0x68 = NTSC */ - R_0E_CHROMA_CNTL_1, 0x07, /* video autodetection is on */ - - R_5A_V_OFF_FOR_SLICER, 0x06, /* standard 60hz value for ITU656 line counting */ - - /* Task A */ - R_90_A_TASK_HANDLING_CNTL, 0x80, - R_91_A_X_PORT_FORMATS_AND_CONF, 0x48, - R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL, 0x40, - R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF, 0x84, - - /* hoffset low (input), 0x0002 is minimum */ - R_94_A_HORIZ_INPUT_WINDOW_START, 0x01, - R_95_A_HORIZ_INPUT_WINDOW_START_MSB, 0x00, - - /* hsize low (input), 0x02d0 = 720 */ - R_96_A_HORIZ_INPUT_WINDOW_LENGTH, 0xd0, - R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02, - - R_98_A_VERT_INPUT_WINDOW_START, 0x05, - R_99_A_VERT_INPUT_WINDOW_START_MSB, 0x00, - - R_9A_A_VERT_INPUT_WINDOW_LENGTH, 0x0c, - R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB, 0x00, - - R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH, 0xa0, - R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x05, - - R_9E_A_VERT_OUTPUT_WINDOW_LENGTH, 0x0c, - R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB, 0x00, - - /* Task B */ - R_C0_B_TASK_HANDLING_CNTL, 0x00, - R_C1_B_X_PORT_FORMATS_AND_CONF, 0x08, - R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION, 0x00, - R_C3_B_I_PORT_FORMATS_AND_CONF, 0x80, - - /* 0x0002 is minimum */ - R_C4_B_HORIZ_INPUT_WINDOW_START, 0x02, - R_C5_B_HORIZ_INPUT_WINDOW_START_MSB, 0x00, - - /* 0x02d0 = 720 */ - R_C6_B_HORIZ_INPUT_WINDOW_LENGTH, 0xd0, - R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02, - - /* vwindow start 0x12 = 18 */ - R_C8_B_VERT_INPUT_WINDOW_START, 0x12, - R_C9_B_VERT_INPUT_WINDOW_START_MSB, 0x00, - - /* vwindow length 0xf8 = 248 */ - R_CA_B_VERT_INPUT_WINDOW_LENGTH, VRES_60HZ>>1, - R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB, VRES_60HZ>>9, - - /* hwindow 0x02d0 = 720 */ - R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, 0xd0, - R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x02, - - R_F0_LFCO_PER_LINE, 0xad, /* Set PLL Register. 60hz 525 lines per frame, 27 MHz */ - R_F1_P_I_PARAM_SELECT, 0x05, /* low bit with 0xF0 */ - R_F5_PULSGEN_LINE_LENGTH, 0xad, - R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG, 0x01, - - 0x00, 0x00 -}; - -static const unsigned char saa7115_cfg_50hz_video[] = { - R_80_GLOBAL_CNTL_1, 0x00, - R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ - - R_15_VGATE_START_FID_CHG, 0x37, /* VGATE start */ - R_16_VGATE_STOP, 0x16, - R_17_MISC_VGATE_CONF_AND_MSB, 0x99, - - R_08_SYNC_CNTL, 0x28, /* 0x28 = PAL */ - R_0E_CHROMA_CNTL_1, 0x07, - - R_5A_V_OFF_FOR_SLICER, 0x03, /* standard 50hz value */ - - /* Task A */ - R_90_A_TASK_HANDLING_CNTL, 0x81, - R_91_A_X_PORT_FORMATS_AND_CONF, 0x48, - R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL, 0x40, - R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF, 0x84, - - /* This is weird: the datasheet says that you should use 2 as the minimum value, */ - /* but Hauppauge uses 0, and changing that to 2 causes indeed problems (for 50hz) */ - /* hoffset low (input), 0x0002 is minimum */ - R_94_A_HORIZ_INPUT_WINDOW_START, 0x00, - R_95_A_HORIZ_INPUT_WINDOW_START_MSB, 0x00, - - /* hsize low (input), 0x02d0 = 720 */ - R_96_A_HORIZ_INPUT_WINDOW_LENGTH, 0xd0, - R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02, - - R_98_A_VERT_INPUT_WINDOW_START, 0x03, - R_99_A_VERT_INPUT_WINDOW_START_MSB, 0x00, - - /* vsize 0x12 = 18 */ - R_9A_A_VERT_INPUT_WINDOW_LENGTH, 0x12, - R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB, 0x00, - - /* hsize 0x05a0 = 1440 */ - R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH, 0xa0, - R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x05, /* hsize hi (output) */ - R_9E_A_VERT_OUTPUT_WINDOW_LENGTH, 0x12, /* vsize low (output), 0x12 = 18 */ - R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB, 0x00, /* vsize hi (output) */ - - /* Task B */ - R_C0_B_TASK_HANDLING_CNTL, 0x00, - R_C1_B_X_PORT_FORMATS_AND_CONF, 0x08, - R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION, 0x00, - R_C3_B_I_PORT_FORMATS_AND_CONF, 0x80, - - /* This is weird: the datasheet says that you should use 2 as the minimum value, */ - /* but Hauppauge uses 0, and changing that to 2 causes indeed problems (for 50hz) */ - /* hoffset low (input), 0x0002 is minimum. See comment above. */ - R_C4_B_HORIZ_INPUT_WINDOW_START, 0x00, - R_C5_B_HORIZ_INPUT_WINDOW_START_MSB, 0x00, - - /* hsize 0x02d0 = 720 */ - R_C6_B_HORIZ_INPUT_WINDOW_LENGTH, 0xd0, - R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02, - - /* voffset 0x16 = 22 */ - R_C8_B_VERT_INPUT_WINDOW_START, 0x16, - R_C9_B_VERT_INPUT_WINDOW_START_MSB, 0x00, - - /* vsize 0x0120 = 288 */ - R_CA_B_VERT_INPUT_WINDOW_LENGTH, 0x20, - R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB, 0x01, - - /* hsize 0x02d0 = 720 */ - R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, 0xd0, - R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x02, - - R_F0_LFCO_PER_LINE, 0xb0, /* Set PLL Register. 50hz 625 lines per frame, 27 MHz */ - R_F1_P_I_PARAM_SELECT, 0x05, /* low bit with 0xF0, (was 0x05) */ - R_F5_PULSGEN_LINE_LENGTH, 0xb0, - R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG, 0x01, - - 0x00, 0x00 -}; - -/* ============== SAA7715 VIDEO templates (end) ======= */ - -static const unsigned char saa7115_cfg_vbi_on[] = { - R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */ - R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ - R_80_GLOBAL_CNTL_1, 0x30, /* Activate both tasks */ - R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */ - R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* Enable I-port output */ - - 0x00, 0x00 -}; - -static const unsigned char saa7115_cfg_vbi_off[] = { - R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */ - R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ - R_80_GLOBAL_CNTL_1, 0x20, /* Activate only task "B" */ - R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */ - R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* Enable I-port output */ - - 0x00, 0x00 -}; - - -static const unsigned char saa7115_init_misc[] = { - R_81_V_SYNC_FLD_ID_SRC_SEL_AND_RETIMED_V_F, 0x01, - R_83_X_PORT_I_O_ENA_AND_OUT_CLK, 0x01, - R_84_I_PORT_SIGNAL_DEF, 0x20, - R_85_I_PORT_SIGNAL_POLAR, 0x21, - R_86_I_PORT_FIFO_FLAG_CNTL_AND_ARBIT, 0xc5, - R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, - - /* Task A */ - R_A0_A_HORIZ_PRESCALING, 0x01, - R_A1_A_ACCUMULATION_LENGTH, 0x00, - R_A2_A_PRESCALER_DC_GAIN_AND_FIR_PREFILTER, 0x00, - - /* Configure controls at nominal value*/ - R_A4_A_LUMA_BRIGHTNESS_CNTL, 0x80, - R_A5_A_LUMA_CONTRAST_CNTL, 0x40, - R_A6_A_CHROMA_SATURATION_CNTL, 0x40, - - /* note: 2 x zoom ensures that VBI lines have same length as video lines. */ - R_A8_A_HORIZ_LUMA_SCALING_INC, 0x00, - R_A9_A_HORIZ_LUMA_SCALING_INC_MSB, 0x02, - - R_AA_A_HORIZ_LUMA_PHASE_OFF, 0x00, - - /* must be horiz lum scaling / 2 */ - R_AC_A_HORIZ_CHROMA_SCALING_INC, 0x00, - R_AD_A_HORIZ_CHROMA_SCALING_INC_MSB, 0x01, - - /* must be offset luma / 2 */ - R_AE_A_HORIZ_CHROMA_PHASE_OFF, 0x00, - - R_B0_A_VERT_LUMA_SCALING_INC, 0x00, - R_B1_A_VERT_LUMA_SCALING_INC_MSB, 0x04, - - R_B2_A_VERT_CHROMA_SCALING_INC, 0x00, - R_B3_A_VERT_CHROMA_SCALING_INC_MSB, 0x04, - - R_B4_A_VERT_SCALING_MODE_CNTL, 0x01, - - R_B8_A_VERT_CHROMA_PHASE_OFF_00, 0x00, - R_B9_A_VERT_CHROMA_PHASE_OFF_01, 0x00, - R_BA_A_VERT_CHROMA_PHASE_OFF_10, 0x00, - R_BB_A_VERT_CHROMA_PHASE_OFF_11, 0x00, - - R_BC_A_VERT_LUMA_PHASE_OFF_00, 0x00, - R_BD_A_VERT_LUMA_PHASE_OFF_01, 0x00, - R_BE_A_VERT_LUMA_PHASE_OFF_10, 0x00, - R_BF_A_VERT_LUMA_PHASE_OFF_11, 0x00, - - /* Task B */ - R_D0_B_HORIZ_PRESCALING, 0x01, - R_D1_B_ACCUMULATION_LENGTH, 0x00, - R_D2_B_PRESCALER_DC_GAIN_AND_FIR_PREFILTER, 0x00, - - /* Configure controls at nominal value*/ - R_D4_B_LUMA_BRIGHTNESS_CNTL, 0x80, - R_D5_B_LUMA_CONTRAST_CNTL, 0x40, - R_D6_B_CHROMA_SATURATION_CNTL, 0x40, - - /* hor lum scaling 0x0400 = 1 */ - R_D8_B_HORIZ_LUMA_SCALING_INC, 0x00, - R_D9_B_HORIZ_LUMA_SCALING_INC_MSB, 0x04, - - R_DA_B_HORIZ_LUMA_PHASE_OFF, 0x00, - - /* must be hor lum scaling / 2 */ - R_DC_B_HORIZ_CHROMA_SCALING, 0x00, - R_DD_B_HORIZ_CHROMA_SCALING_MSB, 0x02, - - /* must be offset luma / 2 */ - R_DE_B_HORIZ_PHASE_OFFSET_CRHOMA, 0x00, - - R_E0_B_VERT_LUMA_SCALING_INC, 0x00, - R_E1_B_VERT_LUMA_SCALING_INC_MSB, 0x04, - - R_E2_B_VERT_CHROMA_SCALING_INC, 0x00, - R_E3_B_VERT_CHROMA_SCALING_INC_MSB, 0x04, - - R_E4_B_VERT_SCALING_MODE_CNTL, 0x01, - - R_E8_B_VERT_CHROMA_PHASE_OFF_00, 0x00, - R_E9_B_VERT_CHROMA_PHASE_OFF_01, 0x00, - R_EA_B_VERT_CHROMA_PHASE_OFF_10, 0x00, - R_EB_B_VERT_CHROMA_PHASE_OFF_11, 0x00, - - R_EC_B_VERT_LUMA_PHASE_OFF_00, 0x00, - R_ED_B_VERT_LUMA_PHASE_OFF_01, 0x00, - R_EE_B_VERT_LUMA_PHASE_OFF_10, 0x00, - R_EF_B_VERT_LUMA_PHASE_OFF_11, 0x00, - - R_F2_NOMINAL_PLL2_DTO, 0x50, /* crystal clock = 24.576 MHz, target = 27MHz */ - R_F3_PLL_INCREMENT, 0x46, - R_F4_PLL2_STATUS, 0x00, - R_F7_PULSE_A_POS_MSB, 0x4b, /* not the recommended settings! */ - R_F8_PULSE_B_POS, 0x00, - R_F9_PULSE_B_POS_MSB, 0x4b, - R_FA_PULSE_C_POS, 0x00, - R_FB_PULSE_C_POS_MSB, 0x4b, - - /* PLL2 lock detection settings: 71 lines 50% phase error */ - R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES, 0x88, - - /* Turn off VBI */ - R_40_SLICER_CNTL_1, 0x20, /* No framing code errors allowed. */ - R_41_LCR_BASE, 0xff, - R_41_LCR_BASE+1, 0xff, - R_41_LCR_BASE+2, 0xff, - R_41_LCR_BASE+3, 0xff, - R_41_LCR_BASE+4, 0xff, - R_41_LCR_BASE+5, 0xff, - R_41_LCR_BASE+6, 0xff, - R_41_LCR_BASE+7, 0xff, - R_41_LCR_BASE+8, 0xff, - R_41_LCR_BASE+9, 0xff, - R_41_LCR_BASE+10, 0xff, - R_41_LCR_BASE+11, 0xff, - R_41_LCR_BASE+12, 0xff, - R_41_LCR_BASE+13, 0xff, - R_41_LCR_BASE+14, 0xff, - R_41_LCR_BASE+15, 0xff, - R_41_LCR_BASE+16, 0xff, - R_41_LCR_BASE+17, 0xff, - R_41_LCR_BASE+18, 0xff, - R_41_LCR_BASE+19, 0xff, - R_41_LCR_BASE+20, 0xff, - R_41_LCR_BASE+21, 0xff, - R_41_LCR_BASE+22, 0xff, - R_58_PROGRAM_FRAMING_CODE, 0x40, - R_59_H_OFF_FOR_SLICER, 0x47, - R_5B_FLD_OFF_AND_MSB_FOR_H_AND_V_OFF, 0x83, - R_5D_DID, 0xbd, - R_5E_SDID, 0x35, - - R_02_INPUT_CNTL_1, 0xc4, /* input tuner -> input 4, amplifier active */ - - R_80_GLOBAL_CNTL_1, 0x20, /* enable task B */ - R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, - R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, - 0x00, 0x00 -}; - -static int saa711x_odd_parity(u8 c) -{ - c ^= (c >> 4); - c ^= (c >> 2); - c ^= (c >> 1); - - return c & 1; -} - -static int saa711x_decode_vps(u8 *dst, u8 *p) -{ - static const u8 biphase_tbl[] = { - 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, - 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, - 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, - 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, - 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, - 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, - 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, - 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, - 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, - 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, - 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87, - 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3, - 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85, - 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1, - 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, - 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, - 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, - 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, - 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86, - 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2, - 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84, - 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0, - 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, - 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, - 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, - 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, - 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, - 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, - 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, - 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, - 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, - 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, - }; - int i; - u8 c, err = 0; - - for (i = 0; i < 2 * 13; i += 2) { - err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]]; - c = (biphase_tbl[p[i + 1]] & 0xf) | ((biphase_tbl[p[i]] & 0xf) << 4); - dst[i / 2] = c; - } - return err & 0xf0; -} - -static int saa711x_decode_wss(u8 *p) -{ - static const int wss_bits[8] = { - 0, 0, 0, 1, 0, 1, 1, 1 - }; - unsigned char parity; - int wss = 0; - int i; - - for (i = 0; i < 16; i++) { - int b1 = wss_bits[p[i] & 7]; - int b2 = wss_bits[(p[i] >> 3) & 7]; - - if (b1 == b2) - return -1; - wss |= b2 << i; - } - parity = wss & 15; - parity ^= parity >> 2; - parity ^= parity >> 1; - - if (!(parity & 1)) - return -1; - - return wss; -} - -static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq) -{ - struct saa711x_state *state = to_state(sd); - u32 acpf; - u32 acni; - u32 hz; - u64 f; - u8 acc = 0; /* reg 0x3a, audio clock control */ - - /* Checks for chips that don't have audio clock (saa7111, saa7113) */ - if (!saa711x_has_reg(state->ident, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD)) - return 0; - - v4l2_dbg(1, debug, sd, "set audio clock freq: %d\n", freq); - - /* sanity check */ - if (freq < 32000 || freq > 48000) - return -EINVAL; - - /* hz is the refresh rate times 100 */ - hz = (state->std & V4L2_STD_525_60) ? 5994 : 5000; - /* acpf = (256 * freq) / field_frequency == (256 * 100 * freq) / hz */ - acpf = (25600 * freq) / hz; - /* acni = (256 * freq * 2^23) / crystal_frequency = - (freq * 2^(8+23)) / crystal_frequency = - (freq << 31) / crystal_frequency */ - f = freq; - f = f << 31; - do_div(f, state->crystal_freq); - acni = f; - if (state->ucgc) { - acpf = acpf * state->cgcdiv / 16; - acni = acni * state->cgcdiv / 16; - acc = 0x80; - if (state->cgcdiv == 3) - acc |= 0x40; - } - if (state->apll) - acc |= 0x08; - - saa711x_write(sd, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03); - saa711x_write(sd, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10); - saa711x_write(sd, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc); - - saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff); - saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+1, - (acpf >> 8) & 0xff); - saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+2, - (acpf >> 16) & 0x03); - - saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC, acni & 0xff); - saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+1, (acni >> 8) & 0xff); - saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+2, (acni >> 16) & 0x3f); - state->audclk_freq = freq; - return 0; -} - -static int saa711x_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - struct saa711x_state *state = to_state(sd); - - switch (ctrl->id) { - case V4L2_CID_CHROMA_AGC: - /* chroma gain cluster */ - if (state->agc->val) - state->gain->val = - saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f; - break; - } - return 0; -} - -static int saa711x_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - struct saa711x_state *state = to_state(sd); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, ctrl->val); - break; - - case V4L2_CID_CONTRAST: - saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, ctrl->val); - break; - - case V4L2_CID_SATURATION: - saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, ctrl->val); - break; - - case V4L2_CID_HUE: - saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, ctrl->val); - break; - - case V4L2_CID_CHROMA_AGC: - /* chroma gain cluster */ - if (state->agc->val) - saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val); - else - saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val | 0x80); - break; - - default: - return -EINVAL; - } - - return 0; -} - -static int saa711x_set_size(struct v4l2_subdev *sd, int width, int height) -{ - struct saa711x_state *state = to_state(sd); - int HPSC, HFSC; - int VSCY; - int res; - int is_50hz = state->std & V4L2_STD_625_50; - int Vsrc = is_50hz ? 576 : 480; - - v4l2_dbg(1, debug, sd, "decoder set size to %ix%i\n", width, height); - - /* FIXME need better bounds checking here */ - if ((width < 1) || (width > 1440)) - return -EINVAL; - if ((height < 1) || (height > Vsrc)) - return -EINVAL; - - if (!saa711x_has_reg(state->ident, R_D0_B_HORIZ_PRESCALING)) { - /* Decoder only supports 720 columns and 480 or 576 lines */ - if (width != 720) - return -EINVAL; - if (height != Vsrc) - return -EINVAL; - } - - state->width = width; - state->height = height; - - if (!saa711x_has_reg(state->ident, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH)) - return 0; - - /* probably have a valid size, let's set it */ - /* Set output width/height */ - /* width */ - - saa711x_write(sd, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, - (u8) (width & 0xff)); - saa711x_write(sd, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, - (u8) ((width >> 8) & 0xff)); - - /* Vertical Scaling uses height/2 */ - res = height / 2; - - /* On 60Hz, it is using a higher Vertical Output Size */ - if (!is_50hz) - res += (VRES_60HZ - 480) >> 1; - - /* height */ - saa711x_write(sd, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH, - (u8) (res & 0xff)); - saa711x_write(sd, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB, - (u8) ((res >> 8) & 0xff)); - - /* Scaling settings */ - /* Hprescaler is floor(inres/outres) */ - HPSC = (int)(720 / width); - /* 0 is not allowed (div. by zero) */ - HPSC = HPSC ? HPSC : 1; - HFSC = (int)((1024 * 720) / (HPSC * width)); - /* FIXME hardcodes to "Task B" - * write H prescaler integer */ - saa711x_write(sd, R_D0_B_HORIZ_PRESCALING, - (u8) (HPSC & 0x3f)); - - v4l2_dbg(1, debug, sd, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC); - /* write H fine-scaling (luminance) */ - saa711x_write(sd, R_D8_B_HORIZ_LUMA_SCALING_INC, - (u8) (HFSC & 0xff)); - saa711x_write(sd, R_D9_B_HORIZ_LUMA_SCALING_INC_MSB, - (u8) ((HFSC >> 8) & 0xff)); - /* write H fine-scaling (chrominance) - * must be lum/2, so i'll just bitshift :) */ - saa711x_write(sd, R_DC_B_HORIZ_CHROMA_SCALING, - (u8) ((HFSC >> 1) & 0xff)); - saa711x_write(sd, R_DD_B_HORIZ_CHROMA_SCALING_MSB, - (u8) ((HFSC >> 9) & 0xff)); - - VSCY = (int)((1024 * Vsrc) / height); - v4l2_dbg(1, debug, sd, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY); - - /* Correct Contrast and Luminance */ - saa711x_write(sd, R_D5_B_LUMA_CONTRAST_CNTL, - (u8) (64 * 1024 / VSCY)); - saa711x_write(sd, R_D6_B_CHROMA_SATURATION_CNTL, - (u8) (64 * 1024 / VSCY)); - - /* write V fine-scaling (luminance) */ - saa711x_write(sd, R_E0_B_VERT_LUMA_SCALING_INC, - (u8) (VSCY & 0xff)); - saa711x_write(sd, R_E1_B_VERT_LUMA_SCALING_INC_MSB, - (u8) ((VSCY >> 8) & 0xff)); - /* write V fine-scaling (chrominance) */ - saa711x_write(sd, R_E2_B_VERT_CHROMA_SCALING_INC, - (u8) (VSCY & 0xff)); - saa711x_write(sd, R_E3_B_VERT_CHROMA_SCALING_INC_MSB, - (u8) ((VSCY >> 8) & 0xff)); - - saa711x_writeregs(sd, saa7115_cfg_reset_scaler); - - /* Activates task "B" */ - saa711x_write(sd, R_80_GLOBAL_CNTL_1, - saa711x_read(sd, R_80_GLOBAL_CNTL_1) | 0x20); - - return 0; -} - -static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct saa711x_state *state = to_state(sd); - - /* Prevent unnecessary standard changes. During a standard - change the I-Port is temporarily disabled. Any devices - reading from that port can get confused. - Note that s_std is also used to switch from - radio to TV mode, so if a s_std is broadcast to - all I2C devices then you do not want to have an unwanted - side-effect here. */ - if (std == state->std) - return; - - state->std = std; - - // This works for NTSC-M, SECAM-L and the 50Hz PAL variants. - if (std & V4L2_STD_525_60) { - v4l2_dbg(1, debug, sd, "decoder set standard 60 Hz\n"); - saa711x_writeregs(sd, saa7115_cfg_60hz_video); - saa711x_set_size(sd, 720, 480); - } else { - v4l2_dbg(1, debug, sd, "decoder set standard 50 Hz\n"); - saa711x_writeregs(sd, saa7115_cfg_50hz_video); - saa711x_set_size(sd, 720, 576); - } - - /* Register 0E - Bits D6-D4 on NO-AUTO mode - (SAA7111 and SAA7113 doesn't have auto mode) - 50 Hz / 625 lines 60 Hz / 525 lines - 000 PAL BGDHI (4.43Mhz) NTSC M (3.58MHz) - 001 NTSC 4.43 (50 Hz) PAL 4.43 (60 Hz) - 010 Combination-PAL N (3.58MHz) NTSC 4.43 (60 Hz) - 011 NTSC N (3.58MHz) PAL M (3.58MHz) - 100 reserved NTSC-Japan (3.58MHz) - */ - if (state->ident <= V4L2_IDENT_SAA7113) { - u8 reg = saa711x_read(sd, R_0E_CHROMA_CNTL_1) & 0x8f; - - if (std == V4L2_STD_PAL_M) { - reg |= 0x30; - } else if (std == V4L2_STD_PAL_Nc) { - reg |= 0x20; - } else if (std == V4L2_STD_PAL_60) { - reg |= 0x10; - } else if (std == V4L2_STD_NTSC_M_JP) { - reg |= 0x40; - } else if (std & V4L2_STD_SECAM) { - reg |= 0x50; - } - saa711x_write(sd, R_0E_CHROMA_CNTL_1, reg); - } else { - /* restart task B if needed */ - int taskb = saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10; - - if (taskb && state->ident == V4L2_IDENT_SAA7114) { - saa711x_writeregs(sd, saa7115_cfg_vbi_on); - } - - /* switch audio mode too! */ - saa711x_s_clock_freq(sd, state->audclk_freq); - } -} - -/* setup the sliced VBI lcr registers according to the sliced VBI format */ -static void saa711x_set_lcr(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt) -{ - struct saa711x_state *state = to_state(sd); - int is_50hz = (state->std & V4L2_STD_625_50); - u8 lcr[24]; - int i, x; - -#if 1 - /* saa7113/7114/7118 VBI support are experimental */ - if (!saa711x_has_reg(state->ident, R_41_LCR_BASE)) - return; - -#else - /* SAA7113 and SAA7118 also should support VBI - Need testing */ - if (state->ident != V4L2_IDENT_SAA7115) - return; -#endif - - for (i = 0; i <= 23; i++) - lcr[i] = 0xff; - - if (fmt == NULL) { - /* raw VBI */ - if (is_50hz) - for (i = 6; i <= 23; i++) - lcr[i] = 0xdd; - else - for (i = 10; i <= 21; i++) - lcr[i] = 0xdd; - } else { - /* sliced VBI */ - /* first clear lines that cannot be captured */ - if (is_50hz) { - for (i = 0; i <= 5; i++) - fmt->service_lines[0][i] = - fmt->service_lines[1][i] = 0; - } - else { - for (i = 0; i <= 9; i++) - fmt->service_lines[0][i] = - fmt->service_lines[1][i] = 0; - for (i = 22; i <= 23; i++) - fmt->service_lines[0][i] = - fmt->service_lines[1][i] = 0; - } - - /* Now set the lcr values according to the specified service */ - for (i = 6; i <= 23; i++) { - lcr[i] = 0; - for (x = 0; x <= 1; x++) { - switch (fmt->service_lines[1-x][i]) { - case 0: - lcr[i] |= 0xf << (4 * x); - break; - case V4L2_SLICED_TELETEXT_B: - lcr[i] |= 1 << (4 * x); - break; - case V4L2_SLICED_CAPTION_525: - lcr[i] |= 4 << (4 * x); - break; - case V4L2_SLICED_WSS_625: - lcr[i] |= 5 << (4 * x); - break; - case V4L2_SLICED_VPS: - lcr[i] |= 7 << (4 * x); - break; - } - } - } - } - - /* write the lcr registers */ - for (i = 2; i <= 23; i++) { - saa711x_write(sd, i - 2 + R_41_LCR_BASE, lcr[i]); - } - - /* enable/disable raw VBI capturing */ - saa711x_writeregs(sd, fmt == NULL ? - saa7115_cfg_vbi_on : - saa7115_cfg_vbi_off); -} - -static int saa711x_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *sliced) -{ - static u16 lcr2vbi[] = { - 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ - 0, V4L2_SLICED_CAPTION_525, /* 4 */ - V4L2_SLICED_WSS_625, 0, /* 5 */ - V4L2_SLICED_VPS, 0, 0, 0, 0, /* 7 */ - 0, 0, 0, 0 - }; - int i; - - memset(sliced, 0, sizeof(*sliced)); - /* done if using raw VBI */ - if (saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10) - return 0; - for (i = 2; i <= 23; i++) { - u8 v = saa711x_read(sd, i - 2 + R_41_LCR_BASE); - - sliced->service_lines[0][i] = lcr2vbi[v >> 4]; - sliced->service_lines[1][i] = lcr2vbi[v & 0xf]; - sliced->service_set |= - sliced->service_lines[0][i] | sliced->service_lines[1][i]; - } - return 0; -} - -static int saa711x_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) -{ - saa711x_set_lcr(sd, NULL); - return 0; -} - -static int saa711x_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt) -{ - saa711x_set_lcr(sd, fmt); - return 0; -} - -static int saa711x_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) -{ - if (fmt->code != V4L2_MBUS_FMT_FIXED) - return -EINVAL; - fmt->field = V4L2_FIELD_INTERLACED; - fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; - return saa711x_set_size(sd, fmt->width, fmt->height); -} - -/* Decode the sliced VBI data stream as created by the saa7115. - The format is described in the saa7115 datasheet in Tables 25 and 26 - and in Figure 33. - The current implementation uses SAV/EAV codes and not the ancillary data - headers. The vbi->p pointer points to the R_5E_SDID byte right after the SAV - code. */ -static int saa711x_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi) -{ - struct saa711x_state *state = to_state(sd); - static const char vbi_no_data_pattern[] = { - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0 - }; - u8 *p = vbi->p; - u32 wss; - int id1, id2; /* the ID1 and ID2 bytes from the internal header */ - - vbi->type = 0; /* mark result as a failure */ - id1 = p[2]; - id2 = p[3]; - /* Note: the field bit is inverted for 60 Hz video */ - if (state->std & V4L2_STD_525_60) - id1 ^= 0x40; - - /* Skip internal header, p now points to the start of the payload */ - p += 4; - vbi->p = p; - - /* calculate field and line number of the VBI packet (1-23) */ - vbi->is_second_field = ((id1 & 0x40) != 0); - vbi->line = (id1 & 0x3f) << 3; - vbi->line |= (id2 & 0x70) >> 4; - - /* Obtain data type */ - id2 &= 0xf; - - /* If the VBI slicer does not detect any signal it will fill up - the payload buffer with 0xa0 bytes. */ - if (!memcmp(p, vbi_no_data_pattern, sizeof(vbi_no_data_pattern))) - return 0; - - /* decode payloads */ - switch (id2) { - case 1: - vbi->type = V4L2_SLICED_TELETEXT_B; - break; - case 4: - if (!saa711x_odd_parity(p[0]) || !saa711x_odd_parity(p[1])) - return 0; - vbi->type = V4L2_SLICED_CAPTION_525; - break; - case 5: - wss = saa711x_decode_wss(p); - if (wss == -1) - return 0; - p[0] = wss & 0xff; - p[1] = wss >> 8; - vbi->type = V4L2_SLICED_WSS_625; - break; - case 7: - if (saa711x_decode_vps(p, p) != 0) - return 0; - vbi->type = V4L2_SLICED_VPS; - break; - default: - break; - } - return 0; -} - -/* ============ SAA7115 AUDIO settings (end) ============= */ - -static int saa711x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct saa711x_state *state = to_state(sd); - int status; - - if (state->radio) - return 0; - status = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); - - v4l2_dbg(1, debug, sd, "status: 0x%02x\n", status); - vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0; - return 0; -} - -static int saa711x_s_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct saa711x_state *state = to_state(sd); - - state->radio = 0; - saa711x_set_v4lstd(sd, std); - return 0; -} - -static int saa711x_s_radio(struct v4l2_subdev *sd) -{ - struct saa711x_state *state = to_state(sd); - - state->radio = 1; - return 0; -} - -static int saa711x_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct saa711x_state *state = to_state(sd); - u8 mask = (state->ident <= V4L2_IDENT_SAA7111A) ? 0xf8 : 0xf0; - - v4l2_dbg(1, debug, sd, "decoder set input %d output %d\n", - input, output); - - /* saa7111/3 does not have these inputs */ - if (state->ident <= V4L2_IDENT_SAA7113 && - (input == SAA7115_COMPOSITE4 || - input == SAA7115_COMPOSITE5)) { - return -EINVAL; - } - if (input > SAA7115_SVIDEO3) - return -EINVAL; - if (state->input == input && state->output == output) - return 0; - v4l2_dbg(1, debug, sd, "now setting %s input %s output\n", - (input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite", - (output == SAA7115_IPORT_ON) ? "iport on" : "iport off"); - state->input = input; - - /* saa7111 has slightly different input numbering */ - if (state->ident <= V4L2_IDENT_SAA7111A) { - if (input >= SAA7115_COMPOSITE4) - input -= 2; - /* saa7111 specific */ - saa711x_write(sd, R_10_CHROMA_CNTL_2, - (saa711x_read(sd, R_10_CHROMA_CNTL_2) & 0x3f) | - ((output & 0xc0) ^ 0x40)); - saa711x_write(sd, R_13_RT_X_PORT_OUT_CNTL, - (saa711x_read(sd, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) | - ((output & 2) ? 0x0a : 0)); - } - - /* select mode */ - saa711x_write(sd, R_02_INPUT_CNTL_1, - (saa711x_read(sd, R_02_INPUT_CNTL_1) & mask) | - input); - - /* bypass chrominance trap for S-Video modes */ - saa711x_write(sd, R_09_LUMA_CNTL, - (saa711x_read(sd, R_09_LUMA_CNTL) & 0x7f) | - (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0)); - - state->output = output; - if (state->ident == V4L2_IDENT_SAA7114 || - state->ident == V4L2_IDENT_SAA7115) { - saa711x_write(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK, - (saa711x_read(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) | - (state->output & 0x01)); - } - return 0; -} - -static int saa711x_s_gpio(struct v4l2_subdev *sd, u32 val) -{ - struct saa711x_state *state = to_state(sd); - - if (state->ident > V4L2_IDENT_SAA7111A) - return -EINVAL; - saa711x_write(sd, 0x11, (saa711x_read(sd, 0x11) & 0x7f) | - (val ? 0x80 : 0)); - return 0; -} - -static int saa711x_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct saa711x_state *state = to_state(sd); - - v4l2_dbg(1, debug, sd, "%s output\n", - enable ? "enable" : "disable"); - - if (state->enable == enable) - return 0; - state->enable = enable; - if (!saa711x_has_reg(state->ident, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED)) - return 0; - saa711x_write(sd, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, state->enable); - return 0; -} - -static int saa711x_s_crystal_freq(struct v4l2_subdev *sd, u32 freq, u32 flags) -{ - struct saa711x_state *state = to_state(sd); - - if (freq != SAA7115_FREQ_32_11_MHZ && freq != SAA7115_FREQ_24_576_MHZ) - return -EINVAL; - state->crystal_freq = freq; - state->cgcdiv = (flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4; - state->ucgc = (flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0; - state->apll = (flags & SAA7115_FREQ_FL_APLL) ? 1 : 0; - saa711x_s_clock_freq(sd, state->audclk_freq); - return 0; -} - -static int saa711x_reset(struct v4l2_subdev *sd, u32 val) -{ - v4l2_dbg(1, debug, sd, "decoder RESET\n"); - saa711x_writeregs(sd, saa7115_cfg_reset_scaler); - return 0; -} - -static int saa711x_g_vbi_data(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *data) -{ - /* Note: the internal field ID is inverted for NTSC, - so data->field 0 maps to the saa7115 even field, - whereas for PAL it maps to the saa7115 odd field. */ - switch (data->id) { - case V4L2_SLICED_WSS_625: - if (saa711x_read(sd, 0x6b) & 0xc0) - return -EIO; - data->data[0] = saa711x_read(sd, 0x6c); - data->data[1] = saa711x_read(sd, 0x6d); - return 0; - case V4L2_SLICED_CAPTION_525: - if (data->field == 0) { - /* CC */ - if (saa711x_read(sd, 0x66) & 0x30) - return -EIO; - data->data[0] = saa711x_read(sd, 0x69); - data->data[1] = saa711x_read(sd, 0x6a); - return 0; - } - /* XDS */ - if (saa711x_read(sd, 0x66) & 0xc0) - return -EIO; - data->data[0] = saa711x_read(sd, 0x67); - data->data[1] = saa711x_read(sd, 0x68); - return 0; - default: - return -EINVAL; - } -} - -static int saa711x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) -{ - struct saa711x_state *state = to_state(sd); - int reg1f, reg1e; - - /* - * The V4L2 core already initializes std with all supported - * Standards. All driver needs to do is to mask it, to remove - * standards that don't apply from the mask - */ - - reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); - v4l2_dbg(1, debug, sd, "Status byte 2 (0x1f)=0x%02x\n", reg1f); - - /* horizontal/vertical not locked */ - if (reg1f & 0x40) - goto ret; - - if (reg1f & 0x20) - *std &= V4L2_STD_525_60; - else - *std &= V4L2_STD_625_50; - - if (state->ident != V4L2_IDENT_SAA7115) - goto ret; - - reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC); - - switch (reg1e & 0x03) { - case 1: - *std &= V4L2_STD_NTSC; - break; - case 2: - /* - * V4L2_STD_PAL just cover the european PAL standards. - * This is wrong, as the device could also be using an - * other PAL standard. - */ - *std &= V4L2_STD_PAL | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | - V4L2_STD_PAL_M | V4L2_STD_PAL_60; - break; - case 3: - *std &= V4L2_STD_SECAM; - break; - default: - /* Can't detect anything */ - break; - } - - v4l2_dbg(1, debug, sd, "Status byte 1 (0x1e)=0x%02x\n", reg1e); - -ret: - v4l2_dbg(1, debug, sd, "detected std mask = %08Lx\n", *std); - - return 0; -} - -static int saa711x_g_input_status(struct v4l2_subdev *sd, u32 *status) -{ - struct saa711x_state *state = to_state(sd); - int reg1e = 0x80; - int reg1f; - - *status = V4L2_IN_ST_NO_SIGNAL; - if (state->ident == V4L2_IDENT_SAA7115) - reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC); - reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); - if ((reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80) - *status = 0; - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int saa711x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - reg->val = saa711x_read(sd, reg->reg & 0xff); - reg->size = 1; - return 0; -} - -static int saa711x_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - saa711x_write(sd, reg->reg & 0xff, reg->val & 0xff); - return 0; -} -#endif - -static int saa711x_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct saa711x_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0); -} - -static int saa711x_log_status(struct v4l2_subdev *sd) -{ - struct saa711x_state *state = to_state(sd); - int reg1e, reg1f; - int signalOk; - int vcr; - - v4l2_info(sd, "Audio frequency: %d Hz\n", state->audclk_freq); - if (state->ident != V4L2_IDENT_SAA7115) { - /* status for the saa7114 */ - reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); - signalOk = (reg1f & 0xc1) == 0x81; - v4l2_info(sd, "Video signal: %s\n", signalOk ? "ok" : "bad"); - v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); - return 0; - } - - /* status for the saa7115 */ - reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC); - reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); - - signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80; - vcr = !(reg1f & 0x10); - - if (state->input >= 6) - v4l2_info(sd, "Input: S-Video %d\n", state->input - 6); - else - v4l2_info(sd, "Input: Composite %d\n", state->input); - v4l2_info(sd, "Video signal: %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad"); - v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); - - switch (reg1e & 0x03) { - case 1: - v4l2_info(sd, "Detected format: NTSC\n"); - break; - case 2: - v4l2_info(sd, "Detected format: PAL\n"); - break; - case 3: - v4l2_info(sd, "Detected format: SECAM\n"); - break; - default: - v4l2_info(sd, "Detected format: BW/No color\n"); - break; - } - v4l2_info(sd, "Width, Height: %d, %d\n", state->width, state->height); - v4l2_ctrl_handler_log_status(&state->hdl, sd->name); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_ctrl_ops saa711x_ctrl_ops = { - .s_ctrl = saa711x_s_ctrl, - .g_volatile_ctrl = saa711x_g_volatile_ctrl, -}; - -static const struct v4l2_subdev_core_ops saa711x_core_ops = { - .log_status = saa711x_log_status, - .g_chip_ident = saa711x_g_chip_ident, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, - .s_std = saa711x_s_std, - .reset = saa711x_reset, - .s_gpio = saa711x_s_gpio, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = saa711x_g_register, - .s_register = saa711x_s_register, -#endif -}; - -static const struct v4l2_subdev_tuner_ops saa711x_tuner_ops = { - .s_radio = saa711x_s_radio, - .g_tuner = saa711x_g_tuner, -}; - -static const struct v4l2_subdev_audio_ops saa711x_audio_ops = { - .s_clock_freq = saa711x_s_clock_freq, -}; - -static const struct v4l2_subdev_video_ops saa711x_video_ops = { - .s_routing = saa711x_s_routing, - .s_crystal_freq = saa711x_s_crystal_freq, - .s_mbus_fmt = saa711x_s_mbus_fmt, - .s_stream = saa711x_s_stream, - .querystd = saa711x_querystd, - .g_input_status = saa711x_g_input_status, -}; - -static const struct v4l2_subdev_vbi_ops saa711x_vbi_ops = { - .g_vbi_data = saa711x_g_vbi_data, - .decode_vbi_line = saa711x_decode_vbi_line, - .g_sliced_fmt = saa711x_g_sliced_fmt, - .s_sliced_fmt = saa711x_s_sliced_fmt, - .s_raw_fmt = saa711x_s_raw_fmt, -}; - -static const struct v4l2_subdev_ops saa711x_ops = { - .core = &saa711x_core_ops, - .tuner = &saa711x_tuner_ops, - .audio = &saa711x_audio_ops, - .video = &saa711x_video_ops, - .vbi = &saa711x_vbi_ops, -}; - -/* ----------------------------------------------------------------------- */ - -static int saa711x_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct saa711x_state *state; - struct v4l2_subdev *sd; - struct v4l2_ctrl_handler *hdl; - int i; - char name[17]; - char chip_id; - int autodetect = !id || id->driver_data == 1; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - for (i = 0; i < 0x0f; i++) { - i2c_smbus_write_byte_data(client, 0, i); - name[i] = (i2c_smbus_read_byte_data(client, 0) & 0x0f) + '0'; - if (name[i] > '9') - name[i] += 'a' - '9' - 1; - } - name[i] = '\0'; - - chip_id = name[5]; - - /* Check whether this chip is part of the saa711x series */ - if (memcmp(name + 1, "f711", 4)) { - v4l_dbg(1, debug, client, "chip found @ 0x%x (ID %s) does not match a known saa711x chip.\n", - client->addr << 1, name); - return -ENODEV; - } - - /* Safety check */ - if (!autodetect && id->name[6] != chip_id) { - v4l_warn(client, "found saa711%c while %s was expected\n", - chip_id, id->name); - } - snprintf(client->name, sizeof(client->name), "saa711%c", chip_id); - v4l_info(client, "saa711%c found (%s) @ 0x%x (%s)\n", chip_id, name, - client->addr << 1, client->adapter->name); - - state = kzalloc(sizeof(struct saa711x_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &saa711x_ops); - - hdl = &state->hdl; - v4l2_ctrl_handler_init(hdl, 6); - /* add in ascending ID order */ - v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); - v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, - V4L2_CID_CONTRAST, 0, 127, 1, 64); - v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, - V4L2_CID_SATURATION, 0, 127, 1, 64); - v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, - V4L2_CID_HUE, -128, 127, 1, 0); - state->agc = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, - V4L2_CID_CHROMA_AGC, 0, 1, 1, 1); - state->gain = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, - V4L2_CID_CHROMA_GAIN, 0, 127, 1, 40); - sd->ctrl_handler = hdl; - if (hdl->error) { - int err = hdl->error; - - v4l2_ctrl_handler_free(hdl); - kfree(state); - return err; - } - v4l2_ctrl_auto_cluster(2, &state->agc, 0, true); - - state->input = -1; - state->output = SAA7115_IPORT_ON; - state->enable = 1; - state->radio = 0; - switch (chip_id) { - case '1': - state->ident = V4L2_IDENT_SAA7111; - if (saa711x_read(sd, R_00_CHIP_VERSION) & 0xf0) { - v4l_info(client, "saa7111a variant found\n"); - state->ident = V4L2_IDENT_SAA7111A; - } - break; - case '3': - state->ident = V4L2_IDENT_SAA7113; - break; - case '4': - state->ident = V4L2_IDENT_SAA7114; - break; - case '5': - state->ident = V4L2_IDENT_SAA7115; - break; - case '8': - state->ident = V4L2_IDENT_SAA7118; - break; - default: - state->ident = V4L2_IDENT_SAA7111; - v4l2_info(sd, "WARNING: Chip is not known - Falling back to saa7111\n"); - break; - } - - state->audclk_freq = 48000; - - v4l2_dbg(1, debug, sd, "writing init values\n"); - - /* init to 60hz/48khz */ - state->crystal_freq = SAA7115_FREQ_24_576_MHZ; - switch (state->ident) { - case V4L2_IDENT_SAA7111: - case V4L2_IDENT_SAA7111A: - saa711x_writeregs(sd, saa7111_init); - break; - case V4L2_IDENT_SAA7113: - saa711x_writeregs(sd, saa7113_init); - break; - default: - state->crystal_freq = SAA7115_FREQ_32_11_MHZ; - saa711x_writeregs(sd, saa7115_init_auto_input); - } - if (state->ident > V4L2_IDENT_SAA7111A) - saa711x_writeregs(sd, saa7115_init_misc); - saa711x_set_v4lstd(sd, V4L2_STD_NTSC); - v4l2_ctrl_handler_setup(hdl); - - v4l2_dbg(1, debug, sd, "status: (1E) 0x%02x, (1F) 0x%02x\n", - saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC), - saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC)); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int saa711x_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(sd->ctrl_handler); - kfree(to_state(sd)); - return 0; -} - -static const struct i2c_device_id saa711x_id[] = { - { "saa7115_auto", 1 }, /* autodetect */ - { "saa7111", 0 }, - { "saa7113", 0 }, - { "saa7114", 0 }, - { "saa7115", 0 }, - { "saa7118", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, saa711x_id); - -static struct i2c_driver saa711x_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "saa7115", - }, - .probe = saa711x_probe, - .remove = saa711x_remove, - .id_table = saa711x_id, -}; - -module_i2c_driver(saa711x_driver); diff --git a/drivers/media/video/saa711x_regs.h b/drivers/media/video/saa711x_regs.h deleted file mode 100644 index 4e5f2eb0a2c..00000000000 --- a/drivers/media/video/saa711x_regs.h +++ /dev/null @@ -1,549 +0,0 @@ -/* saa711x - Philips SAA711x video decoder register specifications - * - * Copyright (c) 2006 Mauro Carvalho Chehab - * - * 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. - */ - -#define R_00_CHIP_VERSION 0x00 -/* Video Decoder */ - /* Video Decoder - Frontend part */ -#define R_01_INC_DELAY 0x01 -#define R_02_INPUT_CNTL_1 0x02 -#define R_03_INPUT_CNTL_2 0x03 -#define R_04_INPUT_CNTL_3 0x04 -#define R_05_INPUT_CNTL_4 0x05 - /* Video Decoder - Decoder part */ -#define R_06_H_SYNC_START 0x06 -#define R_07_H_SYNC_STOP 0x07 -#define R_08_SYNC_CNTL 0x08 -#define R_09_LUMA_CNTL 0x09 -#define R_0A_LUMA_BRIGHT_CNTL 0x0a -#define R_0B_LUMA_CONTRAST_CNTL 0x0b -#define R_0C_CHROMA_SAT_CNTL 0x0c -#define R_0D_CHROMA_HUE_CNTL 0x0d -#define R_0E_CHROMA_CNTL_1 0x0e -#define R_0F_CHROMA_GAIN_CNTL 0x0f -#define R_10_CHROMA_CNTL_2 0x10 -#define R_11_MODE_DELAY_CNTL 0x11 -#define R_12_RT_SIGNAL_CNTL 0x12 -#define R_13_RT_X_PORT_OUT_CNTL 0x13 -#define R_14_ANAL_ADC_COMPAT_CNTL 0x14 -#define R_15_VGATE_START_FID_CHG 0x15 -#define R_16_VGATE_STOP 0x16 -#define R_17_MISC_VGATE_CONF_AND_MSB 0x17 -#define R_18_RAW_DATA_GAIN_CNTL 0x18 -#define R_19_RAW_DATA_OFF_CNTL 0x19 -#define R_1A_COLOR_KILL_LVL_CNTL 0x1a -#define R_1B_MISC_TVVCRDET 0x1b -#define R_1C_ENHAN_COMB_CTRL1 0x1c -#define R_1D_ENHAN_COMB_CTRL2 0x1d -#define R_1E_STATUS_BYTE_1_VD_DEC 0x1e -#define R_1F_STATUS_BYTE_2_VD_DEC 0x1f - -/* Component processing and interrupt masking part */ -#define R_23_INPUT_CNTL_5 0x23 -#define R_24_INPUT_CNTL_6 0x24 -#define R_25_INPUT_CNTL_7 0x25 -#define R_29_COMP_DELAY 0x29 -#define R_2A_COMP_BRIGHT_CNTL 0x2a -#define R_2B_COMP_CONTRAST_CNTL 0x2b -#define R_2C_COMP_SAT_CNTL 0x2c -#define R_2D_INTERRUPT_MASK_1 0x2d -#define R_2E_INTERRUPT_MASK_2 0x2e -#define R_2F_INTERRUPT_MASK_3 0x2f - -/* Audio clock generator part */ -#define R_30_AUD_MAST_CLK_CYCLES_PER_FIELD 0x30 -#define R_34_AUD_MAST_CLK_NOMINAL_INC 0x34 -#define R_38_CLK_RATIO_AMXCLK_TO_ASCLK 0x38 -#define R_39_CLK_RATIO_ASCLK_TO_ALRCLK 0x39 -#define R_3A_AUD_CLK_GEN_BASIC_SETUP 0x3a - -/* General purpose VBI data slicer part */ -#define R_40_SLICER_CNTL_1 0x40 -#define R_41_LCR_BASE 0x41 -#define R_58_PROGRAM_FRAMING_CODE 0x58 -#define R_59_H_OFF_FOR_SLICER 0x59 -#define R_5A_V_OFF_FOR_SLICER 0x5a -#define R_5B_FLD_OFF_AND_MSB_FOR_H_AND_V_OFF 0x5b -#define R_5D_DID 0x5d -#define R_5E_SDID 0x5e -#define R_60_SLICER_STATUS_BYTE_0 0x60 -#define R_61_SLICER_STATUS_BYTE_1 0x61 -#define R_62_SLICER_STATUS_BYTE_2 0x62 - -/* X port, I port and the scaler part */ - /* Task independent global settings */ -#define R_80_GLOBAL_CNTL_1 0x80 -#define R_81_V_SYNC_FLD_ID_SRC_SEL_AND_RETIMED_V_F 0x81 -#define R_83_X_PORT_I_O_ENA_AND_OUT_CLK 0x83 -#define R_84_I_PORT_SIGNAL_DEF 0x84 -#define R_85_I_PORT_SIGNAL_POLAR 0x85 -#define R_86_I_PORT_FIFO_FLAG_CNTL_AND_ARBIT 0x86 -#define R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED 0x87 -#define R_88_POWER_SAVE_ADC_PORT_CNTL 0x88 -#define R_8F_STATUS_INFO_SCALER 0x8f - /* Task A definition */ - /* Basic settings and acquisition window definition */ -#define R_90_A_TASK_HANDLING_CNTL 0x90 -#define R_91_A_X_PORT_FORMATS_AND_CONF 0x91 -#define R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL 0x92 -#define R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF 0x93 -#define R_94_A_HORIZ_INPUT_WINDOW_START 0x94 -#define R_95_A_HORIZ_INPUT_WINDOW_START_MSB 0x95 -#define R_96_A_HORIZ_INPUT_WINDOW_LENGTH 0x96 -#define R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB 0x97 -#define R_98_A_VERT_INPUT_WINDOW_START 0x98 -#define R_99_A_VERT_INPUT_WINDOW_START_MSB 0x99 -#define R_9A_A_VERT_INPUT_WINDOW_LENGTH 0x9a -#define R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB 0x9b -#define R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH 0x9c -#define R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB 0x9d -#define R_9E_A_VERT_OUTPUT_WINDOW_LENGTH 0x9e -#define R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB 0x9f - /* FIR filtering and prescaling */ -#define R_A0_A_HORIZ_PRESCALING 0xa0 -#define R_A1_A_ACCUMULATION_LENGTH 0xa1 -#define R_A2_A_PRESCALER_DC_GAIN_AND_FIR_PREFILTER 0xa2 -#define R_A4_A_LUMA_BRIGHTNESS_CNTL 0xa4 -#define R_A5_A_LUMA_CONTRAST_CNTL 0xa5 -#define R_A6_A_CHROMA_SATURATION_CNTL 0xa6 - /* Horizontal phase scaling */ -#define R_A8_A_HORIZ_LUMA_SCALING_INC 0xa8 -#define R_A9_A_HORIZ_LUMA_SCALING_INC_MSB 0xa9 -#define R_AA_A_HORIZ_LUMA_PHASE_OFF 0xaa -#define R_AC_A_HORIZ_CHROMA_SCALING_INC 0xac -#define R_AD_A_HORIZ_CHROMA_SCALING_INC_MSB 0xad -#define R_AE_A_HORIZ_CHROMA_PHASE_OFF 0xae -#define R_AF_A_HORIZ_CHROMA_PHASE_OFF_MSB 0xaf - /* Vertical scaling */ -#define R_B0_A_VERT_LUMA_SCALING_INC 0xb0 -#define R_B1_A_VERT_LUMA_SCALING_INC_MSB 0xb1 -#define R_B2_A_VERT_CHROMA_SCALING_INC 0xb2 -#define R_B3_A_VERT_CHROMA_SCALING_INC_MSB 0xb3 -#define R_B4_A_VERT_SCALING_MODE_CNTL 0xb4 -#define R_B8_A_VERT_CHROMA_PHASE_OFF_00 0xb8 -#define R_B9_A_VERT_CHROMA_PHASE_OFF_01 0xb9 -#define R_BA_A_VERT_CHROMA_PHASE_OFF_10 0xba -#define R_BB_A_VERT_CHROMA_PHASE_OFF_11 0xbb -#define R_BC_A_VERT_LUMA_PHASE_OFF_00 0xbc -#define R_BD_A_VERT_LUMA_PHASE_OFF_01 0xbd -#define R_BE_A_VERT_LUMA_PHASE_OFF_10 0xbe -#define R_BF_A_VERT_LUMA_PHASE_OFF_11 0xbf - /* Task B definition */ - /* Basic settings and acquisition window definition */ -#define R_C0_B_TASK_HANDLING_CNTL 0xc0 -#define R_C1_B_X_PORT_FORMATS_AND_CONF 0xc1 -#define R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION 0xc2 -#define R_C3_B_I_PORT_FORMATS_AND_CONF 0xc3 -#define R_C4_B_HORIZ_INPUT_WINDOW_START 0xc4 -#define R_C5_B_HORIZ_INPUT_WINDOW_START_MSB 0xc5 -#define R_C6_B_HORIZ_INPUT_WINDOW_LENGTH 0xc6 -#define R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB 0xc7 -#define R_C8_B_VERT_INPUT_WINDOW_START 0xc8 -#define R_C9_B_VERT_INPUT_WINDOW_START_MSB 0xc9 -#define R_CA_B_VERT_INPUT_WINDOW_LENGTH 0xca -#define R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB 0xcb -#define R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH 0xcc -#define R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB 0xcd -#define R_CE_B_VERT_OUTPUT_WINDOW_LENGTH 0xce -#define R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB 0xcf - /* FIR filtering and prescaling */ -#define R_D0_B_HORIZ_PRESCALING 0xd0 -#define R_D1_B_ACCUMULATION_LENGTH 0xd1 -#define R_D2_B_PRESCALER_DC_GAIN_AND_FIR_PREFILTER 0xd2 -#define R_D4_B_LUMA_BRIGHTNESS_CNTL 0xd4 -#define R_D5_B_LUMA_CONTRAST_CNTL 0xd5 -#define R_D6_B_CHROMA_SATURATION_CNTL 0xd6 - /* Horizontal phase scaling */ -#define R_D8_B_HORIZ_LUMA_SCALING_INC 0xd8 -#define R_D9_B_HORIZ_LUMA_SCALING_INC_MSB 0xd9 -#define R_DA_B_HORIZ_LUMA_PHASE_OFF 0xda -#define R_DC_B_HORIZ_CHROMA_SCALING 0xdc -#define R_DD_B_HORIZ_CHROMA_SCALING_MSB 0xdd -#define R_DE_B_HORIZ_PHASE_OFFSET_CRHOMA 0xde - /* Vertical scaling */ -#define R_E0_B_VERT_LUMA_SCALING_INC 0xe0 -#define R_E1_B_VERT_LUMA_SCALING_INC_MSB 0xe1 -#define R_E2_B_VERT_CHROMA_SCALING_INC 0xe2 -#define R_E3_B_VERT_CHROMA_SCALING_INC_MSB 0xe3 -#define R_E4_B_VERT_SCALING_MODE_CNTL 0xe4 -#define R_E8_B_VERT_CHROMA_PHASE_OFF_00 0xe8 -#define R_E9_B_VERT_CHROMA_PHASE_OFF_01 0xe9 -#define R_EA_B_VERT_CHROMA_PHASE_OFF_10 0xea -#define R_EB_B_VERT_CHROMA_PHASE_OFF_11 0xeb -#define R_EC_B_VERT_LUMA_PHASE_OFF_00 0xec -#define R_ED_B_VERT_LUMA_PHASE_OFF_01 0xed -#define R_EE_B_VERT_LUMA_PHASE_OFF_10 0xee -#define R_EF_B_VERT_LUMA_PHASE_OFF_11 0xef - -/* second PLL (PLL2) and Pulsegenerator Programming */ -#define R_F0_LFCO_PER_LINE 0xf0 -#define R_F1_P_I_PARAM_SELECT 0xf1 -#define R_F2_NOMINAL_PLL2_DTO 0xf2 -#define R_F3_PLL_INCREMENT 0xf3 -#define R_F4_PLL2_STATUS 0xf4 -#define R_F5_PULSGEN_LINE_LENGTH 0xf5 -#define R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG 0xf6 -#define R_F7_PULSE_A_POS_MSB 0xf7 -#define R_F8_PULSE_B_POS 0xf8 -#define R_F9_PULSE_B_POS_MSB 0xf9 -#define R_FA_PULSE_C_POS 0xfa -#define R_FB_PULSE_C_POS_MSB 0xfb -#define R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES 0xff - -#if 0 -/* Those structs will be used in the future for debug purposes */ -struct saa711x_reg_descr { - u8 reg; - int count; - char *name; -}; - -struct saa711x_reg_descr saa711x_regs[] = { - /* REG COUNT NAME */ - {R_00_CHIP_VERSION,1, - "Chip version"}, - - /* Video Decoder: R_01_INC_DELAY to R_1F_STATUS_BYTE_2_VD_DEC */ - - /* Video Decoder - Frontend part: R_01_INC_DELAY to R_05_INPUT_CNTL_4 */ - {R_01_INC_DELAY,1, - "Increment delay"}, - {R_02_INPUT_CNTL_1,1, - "Analog input control 1"}, - {R_03_INPUT_CNTL_2,1, - "Analog input control 2"}, - {R_04_INPUT_CNTL_3,1, - "Analog input control 3"}, - {R_05_INPUT_CNTL_4,1, - "Analog input control 4"}, - - /* Video Decoder - Decoder part: R_06_H_SYNC_START to R_1F_STATUS_BYTE_2_VD_DEC */ - {R_06_H_SYNC_START,1, - "Horizontal sync start"}, - {R_07_H_SYNC_STOP,1, - "Horizontal sync stop"}, - {R_08_SYNC_CNTL,1, - "Sync control"}, - {R_09_LUMA_CNTL,1, - "Luminance control"}, - {R_0A_LUMA_BRIGHT_CNTL,1, - "Luminance brightness control"}, - {R_0B_LUMA_CONTRAST_CNTL,1, - "Luminance contrast control"}, - {R_0C_CHROMA_SAT_CNTL,1, - "Chrominance saturation control"}, - {R_0D_CHROMA_HUE_CNTL,1, - "Chrominance hue control"}, - {R_0E_CHROMA_CNTL_1,1, - "Chrominance control 1"}, - {R_0F_CHROMA_GAIN_CNTL,1, - "Chrominance gain control"}, - {R_10_CHROMA_CNTL_2,1, - "Chrominance control 2"}, - {R_11_MODE_DELAY_CNTL,1, - "Mode/delay control"}, - {R_12_RT_SIGNAL_CNTL,1, - "RT signal control"}, - {R_13_RT_X_PORT_OUT_CNTL,1, - "RT/X port output control"}, - {R_14_ANAL_ADC_COMPAT_CNTL,1, - "Analog/ADC/compatibility control"}, - {R_15_VGATE_START_FID_CHG, 1, - "VGATE start FID change"}, - {R_16_VGATE_STOP,1, - "VGATE stop"}, - {R_17_MISC_VGATE_CONF_AND_MSB, 1, - "Miscellaneous VGATE configuration and MSBs"}, - {R_18_RAW_DATA_GAIN_CNTL,1, - "Raw data gain control",}, - {R_19_RAW_DATA_OFF_CNTL,1, - "Raw data offset control",}, - {R_1A_COLOR_KILL_LVL_CNTL,1, - "Color Killer Level Control"}, - { R_1B_MISC_TVVCRDET, 1, - "MISC /TVVCRDET"}, - { R_1C_ENHAN_COMB_CTRL1, 1, - "Enhanced comb ctrl1"}, - { R_1D_ENHAN_COMB_CTRL2, 1, - "Enhanced comb ctrl1"}, - {R_1E_STATUS_BYTE_1_VD_DEC,1, - "Status byte 1 video decoder"}, - {R_1F_STATUS_BYTE_2_VD_DEC,1, - "Status byte 2 video decoder"}, - - /* Component processing and interrupt masking part: 0x20h to R_2F_INTERRUPT_MASK_3 */ - /* 0x20 to 0x22 - Reserved */ - {R_23_INPUT_CNTL_5,1, - "Analog input control 5"}, - {R_24_INPUT_CNTL_6,1, - "Analog input control 6"}, - {R_25_INPUT_CNTL_7,1, - "Analog input control 7"}, - /* 0x26 to 0x28 - Reserved */ - {R_29_COMP_DELAY,1, - "Component delay"}, - {R_2A_COMP_BRIGHT_CNTL,1, - "Component brightness control"}, - {R_2B_COMP_CONTRAST_CNTL,1, - "Component contrast control"}, - {R_2C_COMP_SAT_CNTL,1, - "Component saturation control"}, - {R_2D_INTERRUPT_MASK_1,1, - "Interrupt mask 1"}, - {R_2E_INTERRUPT_MASK_2,1, - "Interrupt mask 2"}, - {R_2F_INTERRUPT_MASK_3,1, - "Interrupt mask 3"}, - - /* Audio clock generator part: R_30_AUD_MAST_CLK_CYCLES_PER_FIELD to 0x3f */ - {R_30_AUD_MAST_CLK_CYCLES_PER_FIELD,3, - "Audio master clock cycles per field"}, - /* 0x33 - Reserved */ - {R_34_AUD_MAST_CLK_NOMINAL_INC,3, - "Audio master clock nominal increment"}, - /* 0x37 - Reserved */ - {R_38_CLK_RATIO_AMXCLK_TO_ASCLK,1, - "Clock ratio AMXCLK to ASCLK"}, - {R_39_CLK_RATIO_ASCLK_TO_ALRCLK,1, - "Clock ratio ASCLK to ALRCLK"}, - {R_3A_AUD_CLK_GEN_BASIC_SETUP,1, - "Audio clock generator basic setup"}, - /* 0x3b-0x3f - Reserved */ - - /* General purpose VBI data slicer part: R_40_SLICER_CNTL_1 to 0x7f */ - {R_40_SLICER_CNTL_1,1, - "Slicer control 1"}, - {R_41_LCR,23, - "R_41_LCR"}, - {R_58_PROGRAM_FRAMING_CODE,1, - "Programmable framing code"}, - {R_59_H_OFF_FOR_SLICER,1, - "Horizontal offset for slicer"}, - {R_5A_V_OFF_FOR_SLICER,1, - "Vertical offset for slicer"}, - {R_5B_FLD_OFF_AND_MSB_FOR_H_AND_V_OFF,1, - "Field offset and MSBs for horizontal and vertical offset"}, - {R_5D_DID,1, - "Header and data identification (R_5D_DID)"}, - {R_5E_SDID,1, - "Sliced data identification (R_5E_SDID) code"}, - {R_60_SLICER_STATUS_BYTE_0,1, - "Slicer status byte 0"}, - {R_61_SLICER_STATUS_BYTE_1,1, - "Slicer status byte 1"}, - {R_62_SLICER_STATUS_BYTE_2,1, - "Slicer status byte 2"}, - /* 0x63-0x7f - Reserved */ - - /* X port, I port and the scaler part: R_80_GLOBAL_CNTL_1 to R_EF_B_VERT_LUMA_PHASE_OFF_11 */ - /* Task independent global settings: R_80_GLOBAL_CNTL_1 to R_8F_STATUS_INFO_SCALER */ - {R_80_GLOBAL_CNTL_1,1, - "Global control 1"}, - {R_81_V_SYNC_FLD_ID_SRC_SEL_AND_RETIMED_V_F,1, - "Vertical sync and Field ID source selection, retimed V and F signals"}, - /* 0x82 - Reserved */ - {R_83_X_PORT_I_O_ENA_AND_OUT_CLK,1, - "X port I/O enable and output clock"}, - {R_84_I_PORT_SIGNAL_DEF,1, - "I port signal definitions"}, - {R_85_I_PORT_SIGNAL_POLAR,1, - "I port signal polarities"}, - {R_86_I_PORT_FIFO_FLAG_CNTL_AND_ARBIT,1, - "I port FIFO flag control and arbitration"}, - {R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 1, - "I port I/O enable output clock and gated"}, - {R_88_POWER_SAVE_ADC_PORT_CNTL,1, - "Power save/ADC port control"}, - /* 089-0x8e - Reserved */ - {R_8F_STATUS_INFO_SCALER,1, - "Status information scaler part"}, - - /* Task A definition: R_90_A_TASK_HANDLING_CNTL to R_BF_A_VERT_LUMA_PHASE_OFF_11 */ - /* Task A: Basic settings and acquisition window definition */ - {R_90_A_TASK_HANDLING_CNTL,1, - "Task A: Task handling control"}, - {R_91_A_X_PORT_FORMATS_AND_CONF,1, - "Task A: X port formats and configuration"}, - {R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL,1, - "Task A: X port input reference signal definition"}, - {R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF,1, - "Task A: I port output formats and configuration"}, - {R_94_A_HORIZ_INPUT_WINDOW_START,2, - "Task A: Horizontal input window start"}, - {R_96_A_HORIZ_INPUT_WINDOW_LENGTH,2, - "Task A: Horizontal input window length"}, - {R_98_A_VERT_INPUT_WINDOW_START,2, - "Task A: Vertical input window start"}, - {R_9A_A_VERT_INPUT_WINDOW_LENGTH,2, - "Task A: Vertical input window length"}, - {R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH,2, - "Task A: Horizontal output window length"}, - {R_9E_A_VERT_OUTPUT_WINDOW_LENGTH,2, - "Task A: Vertical output window length"}, - - /* Task A: FIR filtering and prescaling */ - {R_A0_A_HORIZ_PRESCALING,1, - "Task A: Horizontal prescaling"}, - {R_A1_A_ACCUMULATION_LENGTH,1, - "Task A: Accumulation length"}, - {R_A2_A_PRESCALER_DC_GAIN_AND_FIR_PREFILTER,1, - "Task A: Prescaler DC gain and FIR prefilter"}, - /* 0xa3 - Reserved */ - {R_A4_A_LUMA_BRIGHTNESS_CNTL,1, - "Task A: Luminance brightness control"}, - {R_A5_A_LUMA_CONTRAST_CNTL,1, - "Task A: Luminance contrast control"}, - {R_A6_A_CHROMA_SATURATION_CNTL,1, - "Task A: Chrominance saturation control"}, - /* 0xa7 - Reserved */ - - /* Task A: Horizontal phase scaling */ - {R_A8_A_HORIZ_LUMA_SCALING_INC,2, - "Task A: Horizontal luminance scaling increment"}, - {R_AA_A_HORIZ_LUMA_PHASE_OFF,1, - "Task A: Horizontal luminance phase offset"}, - /* 0xab - Reserved */ - {R_AC_A_HORIZ_CHROMA_SCALING_INC,2, - "Task A: Horizontal chrominance scaling increment"}, - {R_AE_A_HORIZ_CHROMA_PHASE_OFF,1, - "Task A: Horizontal chrominance phase offset"}, - /* 0xaf - Reserved */ - - /* Task A: Vertical scaling */ - {R_B0_A_VERT_LUMA_SCALING_INC,2, - "Task A: Vertical luminance scaling increment"}, - {R_B2_A_VERT_CHROMA_SCALING_INC,2, - "Task A: Vertical chrominance scaling increment"}, - {R_B4_A_VERT_SCALING_MODE_CNTL,1, - "Task A: Vertical scaling mode control"}, - /* 0xb5-0xb7 - Reserved */ - {R_B8_A_VERT_CHROMA_PHASE_OFF_00,1, - "Task A: Vertical chrominance phase offset '00'"}, - {R_B9_A_VERT_CHROMA_PHASE_OFF_01,1, - "Task A: Vertical chrominance phase offset '01'"}, - {R_BA_A_VERT_CHROMA_PHASE_OFF_10,1, - "Task A: Vertical chrominance phase offset '10'"}, - {R_BB_A_VERT_CHROMA_PHASE_OFF_11,1, - "Task A: Vertical chrominance phase offset '11'"}, - {R_BC_A_VERT_LUMA_PHASE_OFF_00,1, - "Task A: Vertical luminance phase offset '00'"}, - {R_BD_A_VERT_LUMA_PHASE_OFF_01,1, - "Task A: Vertical luminance phase offset '01'"}, - {R_BE_A_VERT_LUMA_PHASE_OFF_10,1, - "Task A: Vertical luminance phase offset '10'"}, - {R_BF_A_VERT_LUMA_PHASE_OFF_11,1, - "Task A: Vertical luminance phase offset '11'"}, - - /* Task B definition: R_C0_B_TASK_HANDLING_CNTL to R_EF_B_VERT_LUMA_PHASE_OFF_11 */ - /* Task B: Basic settings and acquisition window definition */ - {R_C0_B_TASK_HANDLING_CNTL,1, - "Task B: Task handling control"}, - {R_C1_B_X_PORT_FORMATS_AND_CONF,1, - "Task B: X port formats and configuration"}, - {R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION,1, - "Task B: Input reference signal definition"}, - {R_C3_B_I_PORT_FORMATS_AND_CONF,1, - "Task B: I port formats and configuration"}, - {R_C4_B_HORIZ_INPUT_WINDOW_START,2, - "Task B: Horizontal input window start"}, - {R_C6_B_HORIZ_INPUT_WINDOW_LENGTH,2, - "Task B: Horizontal input window length"}, - {R_C8_B_VERT_INPUT_WINDOW_START,2, - "Task B: Vertical input window start"}, - {R_CA_B_VERT_INPUT_WINDOW_LENGTH,2, - "Task B: Vertical input window length"}, - {R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH,2, - "Task B: Horizontal output window length"}, - {R_CE_B_VERT_OUTPUT_WINDOW_LENGTH,2, - "Task B: Vertical output window length"}, - - /* Task B: FIR filtering and prescaling */ - {R_D0_B_HORIZ_PRESCALING,1, - "Task B: Horizontal prescaling"}, - {R_D1_B_ACCUMULATION_LENGTH,1, - "Task B: Accumulation length"}, - {R_D2_B_PRESCALER_DC_GAIN_AND_FIR_PREFILTER,1, - "Task B: Prescaler DC gain and FIR prefilter"}, - /* 0xd3 - Reserved */ - {R_D4_B_LUMA_BRIGHTNESS_CNTL,1, - "Task B: Luminance brightness control"}, - {R_D5_B_LUMA_CONTRAST_CNTL,1, - "Task B: Luminance contrast control"}, - {R_D6_B_CHROMA_SATURATION_CNTL,1, - "Task B: Chrominance saturation control"}, - /* 0xd7 - Reserved */ - - /* Task B: Horizontal phase scaling */ - {R_D8_B_HORIZ_LUMA_SCALING_INC,2, - "Task B: Horizontal luminance scaling increment"}, - {R_DA_B_HORIZ_LUMA_PHASE_OFF,1, - "Task B: Horizontal luminance phase offset"}, - /* 0xdb - Reserved */ - {R_DC_B_HORIZ_CHROMA_SCALING,2, - "Task B: Horizontal chrominance scaling"}, - {R_DE_B_HORIZ_PHASE_OFFSET_CRHOMA,1, - "Task B: Horizontal Phase Offset Chroma"}, - /* 0xdf - Reserved */ - - /* Task B: Vertical scaling */ - {R_E0_B_VERT_LUMA_SCALING_INC,2, - "Task B: Vertical luminance scaling increment"}, - {R_E2_B_VERT_CHROMA_SCALING_INC,2, - "Task B: Vertical chrominance scaling increment"}, - {R_E4_B_VERT_SCALING_MODE_CNTL,1, - "Task B: Vertical scaling mode control"}, - /* 0xe5-0xe7 - Reserved */ - {R_E8_B_VERT_CHROMA_PHASE_OFF_00,1, - "Task B: Vertical chrominance phase offset '00'"}, - {R_E9_B_VERT_CHROMA_PHASE_OFF_01,1, - "Task B: Vertical chrominance phase offset '01'"}, - {R_EA_B_VERT_CHROMA_PHASE_OFF_10,1, - "Task B: Vertical chrominance phase offset '10'"}, - {R_EB_B_VERT_CHROMA_PHASE_OFF_11,1, - "Task B: Vertical chrominance phase offset '11'"}, - {R_EC_B_VERT_LUMA_PHASE_OFF_00,1, - "Task B: Vertical luminance phase offset '00'"}, - {R_ED_B_VERT_LUMA_PHASE_OFF_01,1, - "Task B: Vertical luminance phase offset '01'"}, - {R_EE_B_VERT_LUMA_PHASE_OFF_10,1, - "Task B: Vertical luminance phase offset '10'"}, - {R_EF_B_VERT_LUMA_PHASE_OFF_11,1, - "Task B: Vertical luminance phase offset '11'"}, - - /* second PLL (PLL2) and Pulsegenerator Programming */ - { R_F0_LFCO_PER_LINE, 1, - "LFCO's per line"}, - { R_F1_P_I_PARAM_SELECT,1, - "P-/I- Param. Select., PLL Mode, PLL H-Src., LFCO's per line"}, - { R_F2_NOMINAL_PLL2_DTO,1, - "Nominal PLL2 DTO"}, - {R_F3_PLL_INCREMENT,1, - "PLL2 Increment"}, - {R_F4_PLL2_STATUS,1, - "PLL2 Status"}, - {R_F5_PULSGEN_LINE_LENGTH,1, - "Pulsgen. line length"}, - {R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG,1, - "Pulse A Position, Pulsgen Resync., Pulsgen. H-Src., Pulsgen. line length"}, - {R_F7_PULSE_A_POS_MSB,1, - "Pulse A Position"}, - {R_F8_PULSE_B_POS,2, - "Pulse B Position"}, - {R_FA_PULSE_C_POS,2, - "Pulse C Position"}, - /* 0xfc to 0xfe - Reserved */ - {R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES,1, - "S_PLL max. phase, error threshold, PLL2 no. of lines, threshold"}, -}; -#endif diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c deleted file mode 100644 index 8ecb6564a31..00000000000 --- a/drivers/media/video/saa7127.c +++ /dev/null @@ -1,852 +0,0 @@ -/* - * saa7127 - Philips SAA7127/SAA7129 video encoder driver - * - * Copyright (C) 2003 Roy Bulter - * - * Based on SAA7126 video encoder driver by Gillem & Andreas Oberritter - * - * Copyright (C) 2000-2001 Gillem - * Copyright (C) 2002 Andreas Oberritter - * - * Based on Stadis 4:2:2 MPEG-2 Decoder Driver by Nathan Laredo - * - * Copyright (C) 1999 Nathan Laredo - * - * This driver is designed for the Hauppauge 250/350 Linux driver - * from the ivtv Project - * - * Copyright (C) 2003 Kevin Thayer - * - * Dual output support: - * Copyright (C) 2004 Eric Varsanyi - * - * NTSC Tuning and 7.5 IRE Setup - * Copyright (C) 2004 Chris Kennedy - * - * VBI additions & cleanup: - * Copyright (C) 2004, 2005 Hans Verkuil - * - * Note: the saa7126 is identical to the saa7127, and the saa7128 is - * identical to the saa7129, except that the saa7126 and saa7128 have - * macrovision anti-taping support. This driver will almost certainly - * work fine for those chips, except of course for the missing anti-taping - * support. - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -static int debug; -static int test_image; - -MODULE_DESCRIPTION("Philips SAA7127/9 video encoder driver"); -MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil"); -MODULE_LICENSE("GPL"); -module_param(debug, int, 0644); -module_param(test_image, int, 0644); -MODULE_PARM_DESC(debug, "debug level (0-2)"); -MODULE_PARM_DESC(test_image, "test_image (0-1)"); - - -/* - * SAA7127 registers - */ - -#define SAA7127_REG_STATUS 0x00 -#define SAA7127_REG_WIDESCREEN_CONFIG 0x26 -#define SAA7127_REG_WIDESCREEN_ENABLE 0x27 -#define SAA7127_REG_BURST_START 0x28 -#define SAA7127_REG_BURST_END 0x29 -#define SAA7127_REG_COPYGEN_0 0x2a -#define SAA7127_REG_COPYGEN_1 0x2b -#define SAA7127_REG_COPYGEN_2 0x2c -#define SAA7127_REG_OUTPUT_PORT_CONTROL 0x2d -#define SAA7127_REG_GAIN_LUMINANCE_RGB 0x38 -#define SAA7127_REG_GAIN_COLORDIFF_RGB 0x39 -#define SAA7127_REG_INPUT_PORT_CONTROL_1 0x3a -#define SAA7129_REG_FADE_KEY_COL2 0x4f -#define SAA7127_REG_CHROMA_PHASE 0x5a -#define SAA7127_REG_GAINU 0x5b -#define SAA7127_REG_GAINV 0x5c -#define SAA7127_REG_BLACK_LEVEL 0x5d -#define SAA7127_REG_BLANKING_LEVEL 0x5e -#define SAA7127_REG_VBI_BLANKING 0x5f -#define SAA7127_REG_DAC_CONTROL 0x61 -#define SAA7127_REG_BURST_AMP 0x62 -#define SAA7127_REG_SUBC3 0x63 -#define SAA7127_REG_SUBC2 0x64 -#define SAA7127_REG_SUBC1 0x65 -#define SAA7127_REG_SUBC0 0x66 -#define SAA7127_REG_LINE_21_ODD_0 0x67 -#define SAA7127_REG_LINE_21_ODD_1 0x68 -#define SAA7127_REG_LINE_21_EVEN_0 0x69 -#define SAA7127_REG_LINE_21_EVEN_1 0x6a -#define SAA7127_REG_RCV_PORT_CONTROL 0x6b -#define SAA7127_REG_VTRIG 0x6c -#define SAA7127_REG_HTRIG_HI 0x6d -#define SAA7127_REG_MULTI 0x6e -#define SAA7127_REG_CLOSED_CAPTION 0x6f -#define SAA7127_REG_RCV2_OUTPUT_START 0x70 -#define SAA7127_REG_RCV2_OUTPUT_END 0x71 -#define SAA7127_REG_RCV2_OUTPUT_MSBS 0x72 -#define SAA7127_REG_TTX_REQUEST_H_START 0x73 -#define SAA7127_REG_TTX_REQUEST_H_DELAY_LENGTH 0x74 -#define SAA7127_REG_CSYNC_ADVANCE_VSYNC_SHIFT 0x75 -#define SAA7127_REG_TTX_ODD_REQ_VERT_START 0x76 -#define SAA7127_REG_TTX_ODD_REQ_VERT_END 0x77 -#define SAA7127_REG_TTX_EVEN_REQ_VERT_START 0x78 -#define SAA7127_REG_TTX_EVEN_REQ_VERT_END 0x79 -#define SAA7127_REG_FIRST_ACTIVE 0x7a -#define SAA7127_REG_LAST_ACTIVE 0x7b -#define SAA7127_REG_MSB_VERTICAL 0x7c -#define SAA7127_REG_DISABLE_TTX_LINE_LO_0 0x7e -#define SAA7127_REG_DISABLE_TTX_LINE_LO_1 0x7f - -/* - ********************************************************************** - * - * Arrays with configuration parameters for the SAA7127 - * - ********************************************************************** - */ - -struct i2c_reg_value { - unsigned char reg; - unsigned char value; -}; - -static const struct i2c_reg_value saa7129_init_config_extra[] = { - { SAA7127_REG_OUTPUT_PORT_CONTROL, 0x38 }, - { SAA7127_REG_VTRIG, 0xfa }, - { 0, 0 } -}; - -static const struct i2c_reg_value saa7127_init_config_common[] = { - { SAA7127_REG_WIDESCREEN_CONFIG, 0x0d }, - { SAA7127_REG_WIDESCREEN_ENABLE, 0x00 }, - { SAA7127_REG_COPYGEN_0, 0x77 }, - { SAA7127_REG_COPYGEN_1, 0x41 }, - { SAA7127_REG_COPYGEN_2, 0x00 }, /* Macrovision enable/disable */ - { SAA7127_REG_OUTPUT_PORT_CONTROL, 0xbf }, - { SAA7127_REG_GAIN_LUMINANCE_RGB, 0x00 }, - { SAA7127_REG_GAIN_COLORDIFF_RGB, 0x00 }, - { SAA7127_REG_INPUT_PORT_CONTROL_1, 0x80 }, /* for color bars */ - { SAA7127_REG_LINE_21_ODD_0, 0x77 }, - { SAA7127_REG_LINE_21_ODD_1, 0x41 }, - { SAA7127_REG_LINE_21_EVEN_0, 0x88 }, - { SAA7127_REG_LINE_21_EVEN_1, 0x41 }, - { SAA7127_REG_RCV_PORT_CONTROL, 0x12 }, - { SAA7127_REG_VTRIG, 0xf9 }, - { SAA7127_REG_HTRIG_HI, 0x00 }, - { SAA7127_REG_RCV2_OUTPUT_START, 0x41 }, - { SAA7127_REG_RCV2_OUTPUT_END, 0xc3 }, - { SAA7127_REG_RCV2_OUTPUT_MSBS, 0x00 }, - { SAA7127_REG_TTX_REQUEST_H_START, 0x3e }, - { SAA7127_REG_TTX_REQUEST_H_DELAY_LENGTH, 0xb8 }, - { SAA7127_REG_CSYNC_ADVANCE_VSYNC_SHIFT, 0x03 }, - { SAA7127_REG_TTX_ODD_REQ_VERT_START, 0x15 }, - { SAA7127_REG_TTX_ODD_REQ_VERT_END, 0x16 }, - { SAA7127_REG_TTX_EVEN_REQ_VERT_START, 0x15 }, - { SAA7127_REG_TTX_EVEN_REQ_VERT_END, 0x16 }, - { SAA7127_REG_FIRST_ACTIVE, 0x1a }, - { SAA7127_REG_LAST_ACTIVE, 0x01 }, - { SAA7127_REG_MSB_VERTICAL, 0xc0 }, - { SAA7127_REG_DISABLE_TTX_LINE_LO_0, 0x00 }, - { SAA7127_REG_DISABLE_TTX_LINE_LO_1, 0x00 }, - { 0, 0 } -}; - -#define SAA7127_60HZ_DAC_CONTROL 0x15 -static const struct i2c_reg_value saa7127_init_config_60hz[] = { - { SAA7127_REG_BURST_START, 0x19 }, - /* BURST_END is also used as a chip ID in saa7127_probe */ - { SAA7127_REG_BURST_END, 0x1d }, - { SAA7127_REG_CHROMA_PHASE, 0xa3 }, - { SAA7127_REG_GAINU, 0x98 }, - { SAA7127_REG_GAINV, 0xd3 }, - { SAA7127_REG_BLACK_LEVEL, 0x39 }, - { SAA7127_REG_BLANKING_LEVEL, 0x2e }, - { SAA7127_REG_VBI_BLANKING, 0x2e }, - { SAA7127_REG_DAC_CONTROL, 0x15 }, - { SAA7127_REG_BURST_AMP, 0x4d }, - { SAA7127_REG_SUBC3, 0x1f }, - { SAA7127_REG_SUBC2, 0x7c }, - { SAA7127_REG_SUBC1, 0xf0 }, - { SAA7127_REG_SUBC0, 0x21 }, - { SAA7127_REG_MULTI, 0x90 }, - { SAA7127_REG_CLOSED_CAPTION, 0x11 }, - { 0, 0 } -}; - -#define SAA7127_50HZ_PAL_DAC_CONTROL 0x02 -static struct i2c_reg_value saa7127_init_config_50hz_pal[] = { - { SAA7127_REG_BURST_START, 0x21 }, - /* BURST_END is also used as a chip ID in saa7127_probe */ - { SAA7127_REG_BURST_END, 0x1d }, - { SAA7127_REG_CHROMA_PHASE, 0x3f }, - { SAA7127_REG_GAINU, 0x7d }, - { SAA7127_REG_GAINV, 0xaf }, - { SAA7127_REG_BLACK_LEVEL, 0x33 }, - { SAA7127_REG_BLANKING_LEVEL, 0x35 }, - { SAA7127_REG_VBI_BLANKING, 0x35 }, - { SAA7127_REG_DAC_CONTROL, 0x02 }, - { SAA7127_REG_BURST_AMP, 0x2f }, - { SAA7127_REG_SUBC3, 0xcb }, - { SAA7127_REG_SUBC2, 0x8a }, - { SAA7127_REG_SUBC1, 0x09 }, - { SAA7127_REG_SUBC0, 0x2a }, - { SAA7127_REG_MULTI, 0xa0 }, - { SAA7127_REG_CLOSED_CAPTION, 0x00 }, - { 0, 0 } -}; - -#define SAA7127_50HZ_SECAM_DAC_CONTROL 0x08 -static struct i2c_reg_value saa7127_init_config_50hz_secam[] = { - { SAA7127_REG_BURST_START, 0x21 }, - /* BURST_END is also used as a chip ID in saa7127_probe */ - { SAA7127_REG_BURST_END, 0x1d }, - { SAA7127_REG_CHROMA_PHASE, 0x3f }, - { SAA7127_REG_GAINU, 0x6a }, - { SAA7127_REG_GAINV, 0x81 }, - { SAA7127_REG_BLACK_LEVEL, 0x33 }, - { SAA7127_REG_BLANKING_LEVEL, 0x35 }, - { SAA7127_REG_VBI_BLANKING, 0x35 }, - { SAA7127_REG_DAC_CONTROL, 0x08 }, - { SAA7127_REG_BURST_AMP, 0x2f }, - { SAA7127_REG_SUBC3, 0xb2 }, - { SAA7127_REG_SUBC2, 0x3b }, - { SAA7127_REG_SUBC1, 0xa3 }, - { SAA7127_REG_SUBC0, 0x28 }, - { SAA7127_REG_MULTI, 0x90 }, - { SAA7127_REG_CLOSED_CAPTION, 0x00 }, - { 0, 0 } -}; - -/* - ********************************************************************** - * - * Encoder Struct, holds the configuration state of the encoder - * - ********************************************************************** - */ - -struct saa7127_state { - struct v4l2_subdev sd; - v4l2_std_id std; - u32 ident; - enum saa7127_input_type input_type; - enum saa7127_output_type output_type; - int video_enable; - int wss_enable; - u16 wss_mode; - int cc_enable; - u16 cc_data; - int xds_enable; - u16 xds_data; - int vps_enable; - u8 vps_data[5]; - u8 reg_2d; - u8 reg_3a; - u8 reg_3a_cb; /* colorbar bit */ - u8 reg_61; -}; - -static inline struct saa7127_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct saa7127_state, sd); -} - -static const char * const output_strs[] = -{ - "S-Video + Composite", - "Composite", - "S-Video", - "RGB", - "YUV C", - "YUV V" -}; - -static const char * const wss_strs[] = { - "invalid", - "letterbox 14:9 center", - "letterbox 14:9 top", - "invalid", - "letterbox 16:9 top", - "invalid", - "invalid", - "16:9 full format anamorphic", - "4:3 full format", - "invalid", - "invalid", - "letterbox 16:9 center", - "invalid", - "letterbox >16:9 center", - "14:9 full format center", - "invalid", -}; - -/* ----------------------------------------------------------------------- */ - -static int saa7127_read(struct v4l2_subdev *sd, u8 reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_read_byte_data(client, reg); -} - -/* ----------------------------------------------------------------------- */ - -static int saa7127_write(struct v4l2_subdev *sd, u8 reg, u8 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int i; - - for (i = 0; i < 3; i++) { - if (i2c_smbus_write_byte_data(client, reg, val) == 0) - return 0; - } - v4l2_err(sd, "I2C Write Problem\n"); - return -1; -} - -/* ----------------------------------------------------------------------- */ - -static int saa7127_write_inittab(struct v4l2_subdev *sd, - const struct i2c_reg_value *regs) -{ - while (regs->reg != 0) { - saa7127_write(sd, regs->reg, regs->value); - regs++; - } - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int saa7127_set_vps(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) -{ - struct saa7127_state *state = to_state(sd); - int enable = (data->line != 0); - - if (enable && (data->field != 0 || data->line != 16)) - return -EINVAL; - if (state->vps_enable != enable) { - v4l2_dbg(1, debug, sd, "Turn VPS Signal %s\n", enable ? "on" : "off"); - saa7127_write(sd, 0x54, enable << 7); - state->vps_enable = enable; - } - if (!enable) - return 0; - - state->vps_data[0] = data->data[2]; - state->vps_data[1] = data->data[8]; - state->vps_data[2] = data->data[9]; - state->vps_data[3] = data->data[10]; - state->vps_data[4] = data->data[11]; - v4l2_dbg(1, debug, sd, "Set VPS data %*ph\n", 5, state->vps_data); - saa7127_write(sd, 0x55, state->vps_data[0]); - saa7127_write(sd, 0x56, state->vps_data[1]); - saa7127_write(sd, 0x57, state->vps_data[2]); - saa7127_write(sd, 0x58, state->vps_data[3]); - saa7127_write(sd, 0x59, state->vps_data[4]); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int saa7127_set_cc(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) -{ - struct saa7127_state *state = to_state(sd); - u16 cc = data->data[1] << 8 | data->data[0]; - int enable = (data->line != 0); - - if (enable && (data->field != 0 || data->line != 21)) - return -EINVAL; - if (state->cc_enable != enable) { - v4l2_dbg(1, debug, sd, - "Turn CC %s\n", enable ? "on" : "off"); - saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION, - (state->xds_enable << 7) | (enable << 6) | 0x11); - state->cc_enable = enable; - } - if (!enable) - return 0; - - v4l2_dbg(2, debug, sd, "CC data: %04x\n", cc); - saa7127_write(sd, SAA7127_REG_LINE_21_ODD_0, cc & 0xff); - saa7127_write(sd, SAA7127_REG_LINE_21_ODD_1, cc >> 8); - state->cc_data = cc; - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int saa7127_set_xds(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) -{ - struct saa7127_state *state = to_state(sd); - u16 xds = data->data[1] << 8 | data->data[0]; - int enable = (data->line != 0); - - if (enable && (data->field != 1 || data->line != 21)) - return -EINVAL; - if (state->xds_enable != enable) { - v4l2_dbg(1, debug, sd, "Turn XDS %s\n", enable ? "on" : "off"); - saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION, - (enable << 7) | (state->cc_enable << 6) | 0x11); - state->xds_enable = enable; - } - if (!enable) - return 0; - - v4l2_dbg(2, debug, sd, "XDS data: %04x\n", xds); - saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_0, xds & 0xff); - saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_1, xds >> 8); - state->xds_data = xds; - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int saa7127_set_wss(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) -{ - struct saa7127_state *state = to_state(sd); - int enable = (data->line != 0); - - if (enable && (data->field != 0 || data->line != 23)) - return -EINVAL; - if (state->wss_enable != enable) { - v4l2_dbg(1, debug, sd, "Turn WSS %s\n", enable ? "on" : "off"); - saa7127_write(sd, 0x27, enable << 7); - state->wss_enable = enable; - } - if (!enable) - return 0; - - saa7127_write(sd, 0x26, data->data[0]); - saa7127_write(sd, 0x27, 0x80 | (data->data[1] & 0x3f)); - v4l2_dbg(1, debug, sd, - "WSS mode: %s\n", wss_strs[data->data[0] & 0xf]); - state->wss_mode = (data->data[1] & 0x3f) << 8 | data->data[0]; - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int saa7127_set_video_enable(struct v4l2_subdev *sd, int enable) -{ - struct saa7127_state *state = to_state(sd); - - if (enable) { - v4l2_dbg(1, debug, sd, "Enable Video Output\n"); - saa7127_write(sd, 0x2d, state->reg_2d); - saa7127_write(sd, 0x61, state->reg_61); - } else { - v4l2_dbg(1, debug, sd, "Disable Video Output\n"); - saa7127_write(sd, 0x2d, (state->reg_2d & 0xf0)); - saa7127_write(sd, 0x61, (state->reg_61 | 0xc0)); - } - state->video_enable = enable; - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int saa7127_set_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct saa7127_state *state = to_state(sd); - const struct i2c_reg_value *inittab; - - if (std & V4L2_STD_525_60) { - v4l2_dbg(1, debug, sd, "Selecting 60 Hz video Standard\n"); - inittab = saa7127_init_config_60hz; - state->reg_61 = SAA7127_60HZ_DAC_CONTROL; - - } else if (state->ident == V4L2_IDENT_SAA7129 && - (std & V4L2_STD_SECAM) && - !(std & (V4L2_STD_625_50 & ~V4L2_STD_SECAM))) { - - /* If and only if SECAM, with a SAA712[89] */ - v4l2_dbg(1, debug, sd, - "Selecting 50 Hz SECAM video Standard\n"); - inittab = saa7127_init_config_50hz_secam; - state->reg_61 = SAA7127_50HZ_SECAM_DAC_CONTROL; - - } else { - v4l2_dbg(1, debug, sd, "Selecting 50 Hz PAL video Standard\n"); - inittab = saa7127_init_config_50hz_pal; - state->reg_61 = SAA7127_50HZ_PAL_DAC_CONTROL; - } - - /* Write Table */ - saa7127_write_inittab(sd, inittab); - state->std = std; - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int saa7127_set_output_type(struct v4l2_subdev *sd, int output) -{ - struct saa7127_state *state = to_state(sd); - - switch (output) { - case SAA7127_OUTPUT_TYPE_RGB: - state->reg_2d = 0x0f; /* RGB + CVBS (for sync) */ - state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ - break; - - case SAA7127_OUTPUT_TYPE_COMPOSITE: - if (state->ident == V4L2_IDENT_SAA7129) - state->reg_2d = 0x20; /* CVBS only */ - else - state->reg_2d = 0x08; /* 00001000 CVBS only, RGB DAC's off (high impedance mode) */ - state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ - break; - - case SAA7127_OUTPUT_TYPE_SVIDEO: - if (state->ident == V4L2_IDENT_SAA7129) - state->reg_2d = 0x18; /* Y + C */ - else - state->reg_2d = 0xff; /*11111111 croma -> R, luma -> CVBS + G + B */ - state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ - break; - - case SAA7127_OUTPUT_TYPE_YUV_V: - state->reg_2d = 0x4f; /* reg 2D = 01001111, all DAC's on, RGB + VBS */ - state->reg_3a = 0x0b; /* reg 3A = 00001011, bypass RGB-matrix */ - break; - - case SAA7127_OUTPUT_TYPE_YUV_C: - state->reg_2d = 0x0f; /* reg 2D = 00001111, all DAC's on, RGB + CVBS */ - state->reg_3a = 0x0b; /* reg 3A = 00001011, bypass RGB-matrix */ - break; - - case SAA7127_OUTPUT_TYPE_BOTH: - if (state->ident == V4L2_IDENT_SAA7129) - state->reg_2d = 0x38; - else - state->reg_2d = 0xbf; - state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ - break; - - default: - return -EINVAL; - } - v4l2_dbg(1, debug, sd, - "Selecting %s output type\n", output_strs[output]); - - /* Configure Encoder */ - saa7127_write(sd, 0x2d, state->reg_2d); - saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb); - state->output_type = output; - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int saa7127_set_input_type(struct v4l2_subdev *sd, int input) -{ - struct saa7127_state *state = to_state(sd); - - switch (input) { - case SAA7127_INPUT_TYPE_NORMAL: /* avia */ - v4l2_dbg(1, debug, sd, "Selecting Normal Encoder Input\n"); - state->reg_3a_cb = 0; - break; - - case SAA7127_INPUT_TYPE_TEST_IMAGE: /* color bar */ - v4l2_dbg(1, debug, sd, "Selecting Color Bar generator\n"); - state->reg_3a_cb = 0x80; - break; - - default: - return -EINVAL; - } - saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb); - state->input_type = input; - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int saa7127_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct saa7127_state *state = to_state(sd); - - if (state->std == std) - return 0; - return saa7127_set_std(sd, std); -} - -static int saa7127_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct saa7127_state *state = to_state(sd); - int rc = 0; - - if (state->input_type != input) - rc = saa7127_set_input_type(sd, input); - if (rc == 0 && state->output_type != output) - rc = saa7127_set_output_type(sd, output); - return rc; -} - -static int saa7127_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct saa7127_state *state = to_state(sd); - - if (state->video_enable == enable) - return 0; - return saa7127_set_video_enable(sd, enable); -} - -static int saa7127_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt) -{ - struct saa7127_state *state = to_state(sd); - - memset(fmt, 0, sizeof(*fmt)); - if (state->vps_enable) - fmt->service_lines[0][16] = V4L2_SLICED_VPS; - if (state->wss_enable) - fmt->service_lines[0][23] = V4L2_SLICED_WSS_625; - if (state->cc_enable) { - fmt->service_lines[0][21] = V4L2_SLICED_CAPTION_525; - fmt->service_lines[1][21] = V4L2_SLICED_CAPTION_525; - } - fmt->service_set = - (state->vps_enable ? V4L2_SLICED_VPS : 0) | - (state->wss_enable ? V4L2_SLICED_WSS_625 : 0) | - (state->cc_enable ? V4L2_SLICED_CAPTION_525 : 0); - return 0; -} - -static int saa7127_s_vbi_data(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) -{ - switch (data->id) { - case V4L2_SLICED_WSS_625: - return saa7127_set_wss(sd, data); - case V4L2_SLICED_VPS: - return saa7127_set_vps(sd, data); - case V4L2_SLICED_CAPTION_525: - if (data->field == 0) - return saa7127_set_cc(sd, data); - return saa7127_set_xds(sd, data); - default: - return -EINVAL; - } - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int saa7127_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - reg->val = saa7127_read(sd, reg->reg & 0xff); - reg->size = 1; - return 0; -} - -static int saa7127_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - saa7127_write(sd, reg->reg & 0xff, reg->val & 0xff); - return 0; -} -#endif - -static int saa7127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct saa7127_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0); -} - -static int saa7127_log_status(struct v4l2_subdev *sd) -{ - struct saa7127_state *state = to_state(sd); - - v4l2_info(sd, "Standard: %s\n", (state->std & V4L2_STD_525_60) ? "60 Hz" : "50 Hz"); - v4l2_info(sd, "Input: %s\n", state->input_type ? "color bars" : "normal"); - v4l2_info(sd, "Output: %s\n", state->video_enable ? - output_strs[state->output_type] : "disabled"); - v4l2_info(sd, "WSS: %s\n", state->wss_enable ? - wss_strs[state->wss_mode] : "disabled"); - v4l2_info(sd, "VPS: %s\n", state->vps_enable ? "enabled" : "disabled"); - v4l2_info(sd, "CC: %s\n", state->cc_enable ? "enabled" : "disabled"); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops saa7127_core_ops = { - .log_status = saa7127_log_status, - .g_chip_ident = saa7127_g_chip_ident, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = saa7127_g_register, - .s_register = saa7127_s_register, -#endif -}; - -static const struct v4l2_subdev_video_ops saa7127_video_ops = { - .s_std_output = saa7127_s_std_output, - .s_routing = saa7127_s_routing, - .s_stream = saa7127_s_stream, -}; - -static const struct v4l2_subdev_vbi_ops saa7127_vbi_ops = { - .s_vbi_data = saa7127_s_vbi_data, - .g_sliced_fmt = saa7127_g_sliced_fmt, -}; - -static const struct v4l2_subdev_ops saa7127_ops = { - .core = &saa7127_core_ops, - .video = &saa7127_video_ops, - .vbi = &saa7127_vbi_ops, -}; - -/* ----------------------------------------------------------------------- */ - -static int saa7127_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct saa7127_state *state; - struct v4l2_subdev *sd; - struct v4l2_sliced_vbi_data vbi = { 0, 0, 0, 0 }; /* set to disabled */ - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - v4l_dbg(1, debug, client, "detecting saa7127 client on address 0x%x\n", - client->addr << 1); - - state = kzalloc(sizeof(struct saa7127_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &saa7127_ops); - - /* First test register 0: Bits 5-7 are a version ID (should be 0), - and bit 2 should also be 0. - This is rather general, so the second test is more specific and - looks at the 'ending point of burst in clock cycles' which is - 0x1d after a reset and not expected to ever change. */ - if ((saa7127_read(sd, 0) & 0xe4) != 0 || - (saa7127_read(sd, 0x29) & 0x3f) != 0x1d) { - v4l2_dbg(1, debug, sd, "saa7127 not found\n"); - kfree(state); - return -ENODEV; - } - - if (id->driver_data) { /* Chip type is already known */ - state->ident = id->driver_data; - } else { /* Needs detection */ - int read_result; - - /* Detect if it's an saa7129 */ - read_result = saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2); - saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2, 0xaa); - if (saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2) == 0xaa) { - saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2, - read_result); - state->ident = V4L2_IDENT_SAA7129; - strlcpy(client->name, "saa7129", I2C_NAME_SIZE); - } else { - state->ident = V4L2_IDENT_SAA7127; - strlcpy(client->name, "saa7127", I2C_NAME_SIZE); - } - } - - v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, - client->addr << 1, client->adapter->name); - - v4l2_dbg(1, debug, sd, "Configuring encoder\n"); - saa7127_write_inittab(sd, saa7127_init_config_common); - saa7127_set_std(sd, V4L2_STD_NTSC); - saa7127_set_output_type(sd, SAA7127_OUTPUT_TYPE_BOTH); - saa7127_set_vps(sd, &vbi); - saa7127_set_wss(sd, &vbi); - saa7127_set_cc(sd, &vbi); - saa7127_set_xds(sd, &vbi); - if (test_image == 1) - /* The Encoder has an internal Colorbar generator */ - /* This can be used for debugging */ - saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_TEST_IMAGE); - else - saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_NORMAL); - saa7127_set_video_enable(sd, 1); - - if (state->ident == V4L2_IDENT_SAA7129) - saa7127_write_inittab(sd, saa7129_init_config_extra); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int saa7127_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - /* Turn off TV output */ - saa7127_set_video_enable(sd, 0); - kfree(to_state(sd)); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static struct i2c_device_id saa7127_id[] = { - { "saa7127_auto", 0 }, /* auto-detection */ - { "saa7126", V4L2_IDENT_SAA7127 }, - { "saa7127", V4L2_IDENT_SAA7127 }, - { "saa7128", V4L2_IDENT_SAA7129 }, - { "saa7129", V4L2_IDENT_SAA7129 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, saa7127_id); - -static struct i2c_driver saa7127_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "saa7127", - }, - .probe = saa7127_probe, - .remove = saa7127_remove, - .id_table = saa7127_id, -}; - -module_i2c_driver(saa7127_driver); diff --git a/drivers/media/video/saa717x.c b/drivers/media/video/saa717x.c deleted file mode 100644 index 1e84466515a..00000000000 --- a/drivers/media/video/saa717x.c +++ /dev/null @@ -1,1378 +0,0 @@ -/* - * saa717x - Philips SAA717xHL video decoder driver - * - * Based on the saa7115 driver - * - * Changes by Ohta Kyuma - * - Apply to SAA717x,NEC uPD64031,uPD64083. (1/31/2004) - * - * Changes by T.Adachi (tadachi@tadachi-net.com) - * - support audio, video scaler etc, and checked the initialize sequence. - * - * Cleaned up by Hans Verkuil - * - * Note: this is a reversed engineered driver based on captures from - * the I2C bus under Windows. This chip is very similar to the saa7134, - * though. Unfortunately, this driver is currently only working for NTSC. - * - * 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 -#include -#include -#include - -#include -#include -#include -#include - -MODULE_DESCRIPTION("Philips SAA717x audio/video decoder driver"); -MODULE_AUTHOR("K. Ohta, T. Adachi, Hans Verkuil"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - -/* - * Generic i2c probe - * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' - */ - -struct saa717x_state { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - v4l2_std_id std; - int input; - int enable; - int radio; - int playback; - int audio; - int tuner_audio_mode; - int audio_main_mute; - int audio_main_vol_r; - int audio_main_vol_l; - u16 audio_main_bass; - u16 audio_main_treble; - u16 audio_main_volume; - u16 audio_main_balance; - int audio_input; -}; - -static inline struct saa717x_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct saa717x_state, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct saa717x_state, hdl)->sd; -} - -/* ----------------------------------------------------------------------- */ - -/* for audio mode */ -#define TUNER_AUDIO_MONO 0 /* LL */ -#define TUNER_AUDIO_STEREO 1 /* LR */ -#define TUNER_AUDIO_LANG1 2 /* LL */ -#define TUNER_AUDIO_LANG2 3 /* RR */ - -#define SAA717X_NTSC_WIDTH (704) -#define SAA717X_NTSC_HEIGHT (480) - -/* ----------------------------------------------------------------------- */ - -static int saa717x_write(struct v4l2_subdev *sd, u32 reg, u32 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct i2c_adapter *adap = client->adapter; - int fw_addr = reg == 0x454 || (reg >= 0x464 && reg <= 0x478) || reg == 0x480 || reg == 0x488; - unsigned char mm1[6]; - struct i2c_msg msg; - - msg.flags = 0; - msg.addr = client->addr; - mm1[0] = (reg >> 8) & 0xff; - mm1[1] = reg & 0xff; - - if (fw_addr) { - mm1[4] = (value >> 16) & 0xff; - mm1[3] = (value >> 8) & 0xff; - mm1[2] = value & 0xff; - } else { - mm1[2] = value & 0xff; - } - msg.len = fw_addr ? 5 : 3; /* Long Registers have *only* three bytes! */ - msg.buf = mm1; - v4l2_dbg(2, debug, sd, "wrote: reg 0x%03x=%08x\n", reg, value); - return i2c_transfer(adap, &msg, 1) == 1; -} - -static void saa717x_write_regs(struct v4l2_subdev *sd, u32 *data) -{ - while (data[0] || data[1]) { - saa717x_write(sd, data[0], data[1]); - data += 2; - } -} - -static u32 saa717x_read(struct v4l2_subdev *sd, u32 reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct i2c_adapter *adap = client->adapter; - int fw_addr = (reg >= 0x404 && reg <= 0x4b8) || reg == 0x528; - unsigned char mm1[2]; - unsigned char mm2[4] = { 0, 0, 0, 0 }; - struct i2c_msg msgs[2]; - u32 value; - - msgs[0].flags = 0; - msgs[1].flags = I2C_M_RD; - msgs[0].addr = msgs[1].addr = client->addr; - mm1[0] = (reg >> 8) & 0xff; - mm1[1] = reg & 0xff; - msgs[0].len = 2; - msgs[0].buf = mm1; - msgs[1].len = fw_addr ? 3 : 1; /* Multibyte Registers contains *only* 3 bytes */ - msgs[1].buf = mm2; - i2c_transfer(adap, msgs, 2); - - if (fw_addr) - value = (mm2[2] & 0xff) | ((mm2[1] & 0xff) >> 8) | ((mm2[0] & 0xff) >> 16); - else - value = mm2[0] & 0xff; - - v4l2_dbg(2, debug, sd, "read: reg 0x%03x=0x%08x\n", reg, value); - return value; -} - -/* ----------------------------------------------------------------------- */ - -static u32 reg_init_initialize[] = -{ - /* from linux driver */ - 0x101, 0x008, /* Increment delay */ - - 0x103, 0x000, /* Analog input control 2 */ - 0x104, 0x090, /* Analog input control 3 */ - 0x105, 0x090, /* Analog input control 4 */ - 0x106, 0x0eb, /* Horizontal sync start */ - 0x107, 0x0e0, /* Horizontal sync stop */ - 0x109, 0x055, /* Luminance control */ - - 0x10f, 0x02a, /* Chroma gain control */ - 0x110, 0x000, /* Chroma control 2 */ - - 0x114, 0x045, /* analog/ADC */ - - 0x118, 0x040, /* RAW data gain */ - 0x119, 0x080, /* RAW data offset */ - - 0x044, 0x000, /* VBI horizontal input window start (L) TASK A */ - 0x045, 0x000, /* VBI horizontal input window start (H) TASK A */ - 0x046, 0x0cf, /* VBI horizontal input window stop (L) TASK A */ - 0x047, 0x002, /* VBI horizontal input window stop (H) TASK A */ - - 0x049, 0x000, /* VBI vertical input window start (H) TASK A */ - - 0x04c, 0x0d0, /* VBI horizontal output length (L) TASK A */ - 0x04d, 0x002, /* VBI horizontal output length (H) TASK A */ - - 0x064, 0x080, /* Lumina brightness TASK A */ - 0x065, 0x040, /* Luminance contrast TASK A */ - 0x066, 0x040, /* Chroma saturation TASK A */ - /* 067H: Reserved */ - 0x068, 0x000, /* VBI horizontal scaling increment (L) TASK A */ - 0x069, 0x004, /* VBI horizontal scaling increment (H) TASK A */ - 0x06a, 0x000, /* VBI phase offset TASK A */ - - 0x06e, 0x000, /* Horizontal phase offset Luma TASK A */ - 0x06f, 0x000, /* Horizontal phase offset Chroma TASK A */ - - 0x072, 0x000, /* Vertical filter mode TASK A */ - - 0x084, 0x000, /* VBI horizontal input window start (L) TAKS B */ - 0x085, 0x000, /* VBI horizontal input window start (H) TAKS B */ - 0x086, 0x0cf, /* VBI horizontal input window stop (L) TAKS B */ - 0x087, 0x002, /* VBI horizontal input window stop (H) TAKS B */ - - 0x089, 0x000, /* VBI vertical input window start (H) TAKS B */ - - 0x08c, 0x0d0, /* VBI horizontal output length (L) TASK B */ - 0x08d, 0x002, /* VBI horizontal output length (H) TASK B */ - - 0x0a4, 0x080, /* Lumina brightness TASK B */ - 0x0a5, 0x040, /* Luminance contrast TASK B */ - 0x0a6, 0x040, /* Chroma saturation TASK B */ - /* 0A7H reserved */ - 0x0a8, 0x000, /* VBI horizontal scaling increment (L) TASK B */ - 0x0a9, 0x004, /* VBI horizontal scaling increment (H) TASK B */ - 0x0aa, 0x000, /* VBI phase offset TASK B */ - - 0x0ae, 0x000, /* Horizontal phase offset Luma TASK B */ - 0x0af, 0x000, /*Horizontal phase offset Chroma TASK B */ - - 0x0b2, 0x000, /* Vertical filter mode TASK B */ - - 0x00c, 0x000, /* Start point GREEN path */ - 0x00d, 0x000, /* Start point BLUE path */ - 0x00e, 0x000, /* Start point RED path */ - - 0x010, 0x010, /* GREEN path gamma curve --- */ - 0x011, 0x020, - 0x012, 0x030, - 0x013, 0x040, - 0x014, 0x050, - 0x015, 0x060, - 0x016, 0x070, - 0x017, 0x080, - 0x018, 0x090, - 0x019, 0x0a0, - 0x01a, 0x0b0, - 0x01b, 0x0c0, - 0x01c, 0x0d0, - 0x01d, 0x0e0, - 0x01e, 0x0f0, - 0x01f, 0x0ff, /* --- GREEN path gamma curve */ - - 0x020, 0x010, /* BLUE path gamma curve --- */ - 0x021, 0x020, - 0x022, 0x030, - 0x023, 0x040, - 0x024, 0x050, - 0x025, 0x060, - 0x026, 0x070, - 0x027, 0x080, - 0x028, 0x090, - 0x029, 0x0a0, - 0x02a, 0x0b0, - 0x02b, 0x0c0, - 0x02c, 0x0d0, - 0x02d, 0x0e0, - 0x02e, 0x0f0, - 0x02f, 0x0ff, /* --- BLUE path gamma curve */ - - 0x030, 0x010, /* RED path gamma curve --- */ - 0x031, 0x020, - 0x032, 0x030, - 0x033, 0x040, - 0x034, 0x050, - 0x035, 0x060, - 0x036, 0x070, - 0x037, 0x080, - 0x038, 0x090, - 0x039, 0x0a0, - 0x03a, 0x0b0, - 0x03b, 0x0c0, - 0x03c, 0x0d0, - 0x03d, 0x0e0, - 0x03e, 0x0f0, - 0x03f, 0x0ff, /* --- RED path gamma curve */ - - 0x109, 0x085, /* Luminance control */ - - /**** from app start ****/ - 0x584, 0x000, /* AGC gain control */ - 0x585, 0x000, /* Program count */ - 0x586, 0x003, /* Status reset */ - 0x588, 0x0ff, /* Number of audio samples (L) */ - 0x589, 0x00f, /* Number of audio samples (M) */ - 0x58a, 0x000, /* Number of audio samples (H) */ - 0x58b, 0x000, /* Audio select */ - 0x58c, 0x010, /* Audio channel assign1 */ - 0x58d, 0x032, /* Audio channel assign2 */ - 0x58e, 0x054, /* Audio channel assign3 */ - 0x58f, 0x023, /* Audio format */ - 0x590, 0x000, /* SIF control */ - - 0x595, 0x000, /* ?? */ - 0x596, 0x000, /* ?? */ - 0x597, 0x000, /* ?? */ - - 0x464, 0x00, /* Digital input crossbar1 */ - - 0x46c, 0xbbbb10, /* Digital output selection1-3 */ - 0x470, 0x101010, /* Digital output selection4-6 */ - - 0x478, 0x00, /* Sound feature control */ - - 0x474, 0x18, /* Softmute control */ - - 0x454, 0x0425b9, /* Sound Easy programming(reset) */ - 0x454, 0x042539, /* Sound Easy programming(reset) */ - - - /**** common setting( of DVD play, including scaler commands) ****/ - 0x042, 0x003, /* Data path configuration for VBI (TASK A) */ - - 0x082, 0x003, /* Data path configuration for VBI (TASK B) */ - - 0x108, 0x0f8, /* Sync control */ - 0x2a9, 0x0fd, /* ??? */ - 0x102, 0x089, /* select video input "mode 9" */ - 0x111, 0x000, /* Mode/delay control */ - - 0x10e, 0x00a, /* Chroma control 1 */ - - 0x594, 0x002, /* SIF, analog I/O select */ - - 0x454, 0x0425b9, /* Sound */ - 0x454, 0x042539, - - 0x111, 0x000, - 0x10e, 0x00a, - 0x464, 0x000, - 0x300, 0x000, - 0x301, 0x006, - 0x302, 0x000, - 0x303, 0x006, - 0x308, 0x040, - 0x309, 0x000, - 0x30a, 0x000, - 0x30b, 0x000, - 0x000, 0x002, - 0x001, 0x000, - 0x002, 0x000, - 0x003, 0x000, - 0x004, 0x033, - 0x040, 0x01d, - 0x041, 0x001, - 0x042, 0x004, - 0x043, 0x000, - 0x080, 0x01e, - 0x081, 0x001, - 0x082, 0x004, - 0x083, 0x000, - 0x190, 0x018, - 0x115, 0x000, - 0x116, 0x012, - 0x117, 0x018, - 0x04a, 0x011, - 0x08a, 0x011, - 0x04b, 0x000, - 0x08b, 0x000, - 0x048, 0x000, - 0x088, 0x000, - 0x04e, 0x012, - 0x08e, 0x012, - 0x058, 0x012, - 0x098, 0x012, - 0x059, 0x000, - 0x099, 0x000, - 0x05a, 0x003, - 0x09a, 0x003, - 0x05b, 0x001, - 0x09b, 0x001, - 0x054, 0x008, - 0x094, 0x008, - 0x055, 0x000, - 0x095, 0x000, - 0x056, 0x0c7, - 0x096, 0x0c7, - 0x057, 0x002, - 0x097, 0x002, - 0x0ff, 0x0ff, - 0x060, 0x001, - 0x0a0, 0x001, - 0x061, 0x000, - 0x0a1, 0x000, - 0x062, 0x000, - 0x0a2, 0x000, - 0x063, 0x000, - 0x0a3, 0x000, - 0x070, 0x000, - 0x0b0, 0x000, - 0x071, 0x004, - 0x0b1, 0x004, - 0x06c, 0x0e9, - 0x0ac, 0x0e9, - 0x06d, 0x003, - 0x0ad, 0x003, - 0x05c, 0x0d0, - 0x09c, 0x0d0, - 0x05d, 0x002, - 0x09d, 0x002, - 0x05e, 0x0f2, - 0x09e, 0x0f2, - 0x05f, 0x000, - 0x09f, 0x000, - 0x074, 0x000, - 0x0b4, 0x000, - 0x075, 0x000, - 0x0b5, 0x000, - 0x076, 0x000, - 0x0b6, 0x000, - 0x077, 0x000, - 0x0b7, 0x000, - 0x195, 0x008, - 0x0ff, 0x0ff, - 0x108, 0x0f8, - 0x111, 0x000, - 0x10e, 0x00a, - 0x2a9, 0x0fd, - 0x464, 0x001, - 0x454, 0x042135, - 0x598, 0x0e7, - 0x599, 0x07d, - 0x59a, 0x018, - 0x59c, 0x066, - 0x59d, 0x090, - 0x59e, 0x001, - 0x584, 0x000, - 0x585, 0x000, - 0x586, 0x003, - 0x588, 0x0ff, - 0x589, 0x00f, - 0x58a, 0x000, - 0x58b, 0x000, - 0x58c, 0x010, - 0x58d, 0x032, - 0x58e, 0x054, - 0x58f, 0x023, - 0x590, 0x000, - 0x595, 0x000, - 0x596, 0x000, - 0x597, 0x000, - 0x464, 0x000, - 0x46c, 0xbbbb10, - 0x470, 0x101010, - - - 0x478, 0x000, - 0x474, 0x018, - 0x454, 0x042135, - 0x598, 0x0e7, - 0x599, 0x07d, - 0x59a, 0x018, - 0x59c, 0x066, - 0x59d, 0x090, - 0x59e, 0x001, - 0x584, 0x000, - 0x585, 0x000, - 0x586, 0x003, - 0x588, 0x0ff, - 0x589, 0x00f, - 0x58a, 0x000, - 0x58b, 0x000, - 0x58c, 0x010, - 0x58d, 0x032, - 0x58e, 0x054, - 0x58f, 0x023, - 0x590, 0x000, - 0x595, 0x000, - 0x596, 0x000, - 0x597, 0x000, - 0x464, 0x000, - 0x46c, 0xbbbb10, - 0x470, 0x101010, - - 0x478, 0x000, - 0x474, 0x018, - 0x454, 0x042135, - 0x598, 0x0e7, - 0x599, 0x07d, - 0x59a, 0x018, - 0x59c, 0x066, - 0x59d, 0x090, - 0x59e, 0x001, - 0x584, 0x000, - 0x585, 0x000, - 0x586, 0x003, - 0x588, 0x0ff, - 0x589, 0x00f, - 0x58a, 0x000, - 0x58b, 0x000, - 0x58c, 0x010, - 0x58d, 0x032, - 0x58e, 0x054, - 0x58f, 0x023, - 0x590, 0x000, - 0x595, 0x000, - 0x596, 0x000, - 0x597, 0x000, - 0x464, 0x000, - 0x46c, 0xbbbb10, - 0x470, 0x101010, - 0x478, 0x000, - 0x474, 0x018, - 0x454, 0x042135, - 0x193, 0x000, - 0x300, 0x000, - 0x301, 0x006, - 0x302, 0x000, - 0x303, 0x006, - 0x308, 0x040, - 0x309, 0x000, - 0x30a, 0x000, - 0x30b, 0x000, - 0x000, 0x002, - 0x001, 0x000, - 0x002, 0x000, - 0x003, 0x000, - 0x004, 0x033, - 0x040, 0x01d, - 0x041, 0x001, - 0x042, 0x004, - 0x043, 0x000, - 0x080, 0x01e, - 0x081, 0x001, - 0x082, 0x004, - 0x083, 0x000, - 0x190, 0x018, - 0x115, 0x000, - 0x116, 0x012, - 0x117, 0x018, - 0x04a, 0x011, - 0x08a, 0x011, - 0x04b, 0x000, - 0x08b, 0x000, - 0x048, 0x000, - 0x088, 0x000, - 0x04e, 0x012, - 0x08e, 0x012, - 0x058, 0x012, - 0x098, 0x012, - 0x059, 0x000, - 0x099, 0x000, - 0x05a, 0x003, - 0x09a, 0x003, - 0x05b, 0x001, - 0x09b, 0x001, - 0x054, 0x008, - 0x094, 0x008, - 0x055, 0x000, - 0x095, 0x000, - 0x056, 0x0c7, - 0x096, 0x0c7, - 0x057, 0x002, - 0x097, 0x002, - 0x060, 0x001, - 0x0a0, 0x001, - 0x061, 0x000, - 0x0a1, 0x000, - 0x062, 0x000, - 0x0a2, 0x000, - 0x063, 0x000, - 0x0a3, 0x000, - 0x070, 0x000, - 0x0b0, 0x000, - 0x071, 0x004, - 0x0b1, 0x004, - 0x06c, 0x0e9, - 0x0ac, 0x0e9, - 0x06d, 0x003, - 0x0ad, 0x003, - 0x05c, 0x0d0, - 0x09c, 0x0d0, - 0x05d, 0x002, - 0x09d, 0x002, - 0x05e, 0x0f2, - 0x09e, 0x0f2, - 0x05f, 0x000, - 0x09f, 0x000, - 0x074, 0x000, - 0x0b4, 0x000, - 0x075, 0x000, - 0x0b5, 0x000, - 0x076, 0x000, - 0x0b6, 0x000, - 0x077, 0x000, - 0x0b7, 0x000, - 0x195, 0x008, - 0x598, 0x0e7, - 0x599, 0x07d, - 0x59a, 0x018, - 0x59c, 0x066, - 0x59d, 0x090, - 0x59e, 0x001, - 0x584, 0x000, - 0x585, 0x000, - 0x586, 0x003, - 0x588, 0x0ff, - 0x589, 0x00f, - 0x58a, 0x000, - 0x58b, 0x000, - 0x58c, 0x010, - 0x58d, 0x032, - 0x58e, 0x054, - 0x58f, 0x023, - 0x590, 0x000, - 0x595, 0x000, - 0x596, 0x000, - 0x597, 0x000, - 0x464, 0x000, - 0x46c, 0xbbbb10, - 0x470, 0x101010, - 0x478, 0x000, - 0x474, 0x018, - 0x454, 0x042135, - 0x193, 0x0a6, - 0x108, 0x0f8, - 0x042, 0x003, - 0x082, 0x003, - 0x454, 0x0425b9, - 0x454, 0x042539, - 0x193, 0x000, - 0x193, 0x0a6, - 0x464, 0x000, - - 0, 0 -}; - -/* Tuner */ -static u32 reg_init_tuner_input[] = { - 0x108, 0x0f8, /* Sync control */ - 0x111, 0x000, /* Mode/delay control */ - 0x10e, 0x00a, /* Chroma control 1 */ - 0, 0 -}; - -/* Composite */ -static u32 reg_init_composite_input[] = { - 0x108, 0x0e8, /* Sync control */ - 0x111, 0x000, /* Mode/delay control */ - 0x10e, 0x04a, /* Chroma control 1 */ - 0, 0 -}; - -/* S-Video */ -static u32 reg_init_svideo_input[] = { - 0x108, 0x0e8, /* Sync control */ - 0x111, 0x000, /* Mode/delay control */ - 0x10e, 0x04a, /* Chroma control 1 */ - 0, 0 -}; - -static u32 reg_set_audio_template[4][2] = -{ - { /* for MONO - tadachi 6/29 DMA audio output select? - Register 0x46c - 7-4: DMA2, 3-0: DMA1 ch. DMA4, DMA3 DMA2, DMA1 - 0: MAIN left, 1: MAIN right - 2: AUX1 left, 3: AUX1 right - 4: AUX2 left, 5: AUX2 right - 6: DPL left, 7: DPL right - 8: DPL center, 9: DPL surround - A: monitor output, B: digital sense */ - 0xbbbb00, - - /* tadachi 6/29 DAC and I2S output select? - Register 0x470 - 7-4:DAC right ch. 3-0:DAC left ch. - I2S1 right,left I2S2 right,left */ - 0x00, - }, - { /* for STEREO */ - 0xbbbb10, 0x101010, - }, - { /* for LANG1 */ - 0xbbbb00, 0x00, - }, - { /* for LANG2/SAP */ - 0xbbbb11, 0x111111, - } -}; - - -/* Get detected audio flags (from saa7134 driver) */ -static void get_inf_dev_status(struct v4l2_subdev *sd, - int *dual_flag, int *stereo_flag) -{ - u32 reg_data3; - - 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]", - }; - - - *dual_flag = *stereo_flag = 0; - - /* (demdec status: 0x528) */ - - /* read current status */ - reg_data3 = saa717x_read(sd, 0x0528); - - v4l2_dbg(1, debug, sd, "tvaudio thread status: 0x%x [%s%s%s]\n", - reg_data3, stdres[reg_data3 & 0x1f], - (reg_data3 & 0x000020) ? ",stereo" : "", - (reg_data3 & 0x000040) ? ",dual" : ""); - v4l2_dbg(1, debug, sd, "detailed status: " - "%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s\n", - (reg_data3 & 0x000080) ? " A2/EIAJ pilot tone " : "", - (reg_data3 & 0x000100) ? " A2/EIAJ dual " : "", - (reg_data3 & 0x000200) ? " A2/EIAJ stereo " : "", - (reg_data3 & 0x000400) ? " A2/EIAJ noise mute " : "", - - (reg_data3 & 0x000800) ? " BTSC/FM radio pilot " : "", - (reg_data3 & 0x001000) ? " SAP carrier " : "", - (reg_data3 & 0x002000) ? " BTSC stereo noise mute " : "", - (reg_data3 & 0x004000) ? " SAP noise mute " : "", - (reg_data3 & 0x008000) ? " VDSP " : "", - - (reg_data3 & 0x010000) ? " NICST " : "", - (reg_data3 & 0x020000) ? " NICDU " : "", - (reg_data3 & 0x040000) ? " NICAM muted " : "", - (reg_data3 & 0x080000) ? " NICAM reserve sound " : "", - - (reg_data3 & 0x100000) ? " init done " : ""); - - if (reg_data3 & 0x000220) { - v4l2_dbg(1, debug, sd, "ST!!!\n"); - *stereo_flag = 1; - } - - if (reg_data3 & 0x000140) { - v4l2_dbg(1, debug, sd, "DUAL!!!\n"); - *dual_flag = 1; - } -} - -/* regs write to set audio mode */ -static void set_audio_mode(struct v4l2_subdev *sd, int audio_mode) -{ - v4l2_dbg(1, debug, sd, "writing registers to set audio mode by set %d\n", - audio_mode); - - saa717x_write(sd, 0x46c, reg_set_audio_template[audio_mode][0]); - saa717x_write(sd, 0x470, reg_set_audio_template[audio_mode][1]); -} - -/* write regs to set audio volume, bass and treble */ -static int set_audio_regs(struct v4l2_subdev *sd, - struct saa717x_state *decoder) -{ - u8 mute = 0xac; /* -84 dB */ - u32 val; - unsigned int work_l, work_r; - - /* set SIF analog I/O select */ - saa717x_write(sd, 0x0594, decoder->audio_input); - v4l2_dbg(1, debug, sd, "set audio input %d\n", - decoder->audio_input); - - /* normalize ( 65535 to 0 -> 24 to -40 (not -84)) */ - work_l = (min(65536 - decoder->audio_main_balance, 32768) * decoder->audio_main_volume) / 32768; - work_r = (min(decoder->audio_main_balance, (u16)32768) * decoder->audio_main_volume) / 32768; - decoder->audio_main_vol_l = (long)work_l * (24 - (-40)) / 65535 - 40; - decoder->audio_main_vol_r = (long)work_r * (24 - (-40)) / 65535 - 40; - - /* set main volume */ - /* main volume L[7-0],R[7-0],0x00 24=24dB,-83dB, -84(mute) */ - /* def:0dB->6dB(MPG600GR) */ - /* if mute is on, set mute */ - if (decoder->audio_main_mute) { - val = mute | (mute << 8); - } else { - val = (u8)decoder->audio_main_vol_l | - ((u8)decoder->audio_main_vol_r << 8); - } - - saa717x_write(sd, 0x480, val); - - /* set bass and treble */ - val = decoder->audio_main_bass & 0x1f; - val |= (decoder->audio_main_treble & 0x1f) << 5; - saa717x_write(sd, 0x488, val); - return 0; -} - -/********** scaling staff ***********/ -static void set_h_prescale(struct v4l2_subdev *sd, - 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, task_shift; - - task_shift = task * 0x40; - for (i = 0; i < count; i++) - if (vals[i].xpsc == prescale) - break; - if (i == count) - return; - - /* horizonal prescaling */ - saa717x_write(sd, 0x60 + task_shift, vals[i].xpsc); - /* accumulation length */ - saa717x_write(sd, 0x61 + task_shift, vals[i].xacl); - /* level control */ - saa717x_write(sd, 0x62 + task_shift, - (vals[i].xc2_1 << 3) | vals[i].xdcg); - /*FIR prefilter control */ - saa717x_write(sd, 0x63 + task_shift, - (vals[i].vpfy << 2) | vals[i].vpfy); -} - -/********** scaling staff ***********/ -static void set_v_scale(struct v4l2_subdev *sd, int task, int yscale) -{ - int task_shift; - - task_shift = task * 0x40; - /* Vertical scaling ratio (LOW) */ - saa717x_write(sd, 0x70 + task_shift, yscale & 0xff); - /* Vertical scaling ratio (HI) */ - saa717x_write(sd, 0x71 + task_shift, yscale >> 8); -} - -static int saa717x_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - struct saa717x_state *state = to_state(sd); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - saa717x_write(sd, 0x10a, ctrl->val); - return 0; - - case V4L2_CID_CONTRAST: - saa717x_write(sd, 0x10b, ctrl->val); - return 0; - - case V4L2_CID_SATURATION: - saa717x_write(sd, 0x10c, ctrl->val); - return 0; - - case V4L2_CID_HUE: - saa717x_write(sd, 0x10d, ctrl->val); - return 0; - - case V4L2_CID_AUDIO_MUTE: - state->audio_main_mute = ctrl->val; - break; - - case V4L2_CID_AUDIO_VOLUME: - state->audio_main_volume = ctrl->val; - break; - - case V4L2_CID_AUDIO_BALANCE: - state->audio_main_balance = ctrl->val; - break; - - case V4L2_CID_AUDIO_TREBLE: - state->audio_main_treble = ctrl->val; - break; - - case V4L2_CID_AUDIO_BASS: - state->audio_main_bass = ctrl->val; - break; - - default: - return 0; - } - set_audio_regs(sd, state); - return 0; -} - -static int saa717x_s_video_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct saa717x_state *decoder = to_state(sd); - int is_tuner = input & 0x80; /* tuner input flag */ - - input &= 0x7f; - - v4l2_dbg(1, debug, sd, "decoder set input (%d)\n", input); - /* inputs from 0-9 are available*/ - /* saa717x have mode0-mode9 but mode5 is reserved. */ - if (input > 9 || input == 5) - return -EINVAL; - - if (decoder->input != input) { - int input_line = input; - - decoder->input = input_line; - v4l2_dbg(1, debug, sd, "now setting %s input %d\n", - input_line >= 6 ? "S-Video" : "Composite", - input_line); - - /* select mode */ - saa717x_write(sd, 0x102, - (saa717x_read(sd, 0x102) & 0xf0) | - input_line); - - /* bypass chrominance trap for modes 6..9 */ - saa717x_write(sd, 0x109, - (saa717x_read(sd, 0x109) & 0x7f) | - (input_line < 6 ? 0x0 : 0x80)); - - /* change audio_mode */ - if (is_tuner) { - /* tuner */ - set_audio_mode(sd, decoder->tuner_audio_mode); - } else { - /* Force to STEREO mode if Composite or - * S-Video were chosen */ - set_audio_mode(sd, TUNER_AUDIO_STEREO); - } - /* change initialize procedure (Composite/S-Video) */ - if (is_tuner) - saa717x_write_regs(sd, reg_init_tuner_input); - else if (input_line >= 6) - saa717x_write_regs(sd, reg_init_svideo_input); - else - saa717x_write_regs(sd, reg_init_composite_input); - } - - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int saa717x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - reg->val = saa717x_read(sd, reg->reg); - reg->size = 1; - return 0; -} - -static int saa717x_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u16 addr = reg->reg & 0xffff; - u8 val = reg->val & 0xff; - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - saa717x_write(sd, addr, val); - return 0; -} -#endif - -static int saa717x_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) -{ - int prescale, h_scale, v_scale; - - v4l2_dbg(1, debug, sd, "decoder set size\n"); - - if (fmt->code != V4L2_MBUS_FMT_FIXED) - return -EINVAL; - - /* FIXME need better bounds checking here */ - if (fmt->width < 1 || fmt->width > 1440) - return -EINVAL; - if (fmt->height < 1 || fmt->height > 960) - return -EINVAL; - - fmt->field = V4L2_FIELD_INTERLACED; - fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; - - /* scaling setting */ - /* NTSC and interlace only */ - prescale = SAA717X_NTSC_WIDTH / fmt->width; - if (prescale == 0) - prescale = 1; - h_scale = 1024 * SAA717X_NTSC_WIDTH / prescale / fmt->width; - /* interlace */ - v_scale = 512 * 2 * SAA717X_NTSC_HEIGHT / fmt->height; - - /* Horizontal prescaling etc */ - set_h_prescale(sd, 0, prescale); - set_h_prescale(sd, 1, prescale); - - /* Horizontal scaling increment */ - /* TASK A */ - saa717x_write(sd, 0x6C, (u8)(h_scale & 0xFF)); - saa717x_write(sd, 0x6D, (u8)((h_scale >> 8) & 0xFF)); - /* TASK B */ - saa717x_write(sd, 0xAC, (u8)(h_scale & 0xFF)); - saa717x_write(sd, 0xAD, (u8)((h_scale >> 8) & 0xFF)); - - /* Vertical prescaling etc */ - set_v_scale(sd, 0, v_scale); - set_v_scale(sd, 1, v_scale); - - /* set video output size */ - /* video number of pixels at output */ - /* TASK A */ - saa717x_write(sd, 0x5C, (u8)(fmt->width & 0xFF)); - saa717x_write(sd, 0x5D, (u8)((fmt->width >> 8) & 0xFF)); - /* TASK B */ - saa717x_write(sd, 0x9C, (u8)(fmt->width & 0xFF)); - saa717x_write(sd, 0x9D, (u8)((fmt->width >> 8) & 0xFF)); - - /* video number of lines at output */ - /* TASK A */ - saa717x_write(sd, 0x5E, (u8)(fmt->height & 0xFF)); - saa717x_write(sd, 0x5F, (u8)((fmt->height >> 8) & 0xFF)); - /* TASK B */ - saa717x_write(sd, 0x9E, (u8)(fmt->height & 0xFF)); - saa717x_write(sd, 0x9F, (u8)((fmt->height >> 8) & 0xFF)); - return 0; -} - -static int saa717x_s_radio(struct v4l2_subdev *sd) -{ - struct saa717x_state *decoder = to_state(sd); - - decoder->radio = 1; - return 0; -} - -static int saa717x_s_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct saa717x_state *decoder = to_state(sd); - - v4l2_dbg(1, debug, sd, "decoder set norm "); - v4l2_dbg(1, debug, sd, "(not yet implementd)\n"); - - decoder->radio = 0; - decoder->std = std; - return 0; -} - -static int saa717x_s_audio_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct saa717x_state *decoder = to_state(sd); - - if (input < 3) { /* FIXME! --tadachi */ - decoder->audio_input = input; - v4l2_dbg(1, debug, sd, - "set decoder audio input to %d\n", - decoder->audio_input); - set_audio_regs(sd, decoder); - return 0; - } - return -ERANGE; -} - -static int saa717x_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct saa717x_state *decoder = to_state(sd); - - v4l2_dbg(1, debug, sd, "decoder %s output\n", - enable ? "enable" : "disable"); - decoder->enable = enable; - saa717x_write(sd, 0x193, enable ? 0xa6 : 0x26); - return 0; -} - -/* change audio mode */ -static int saa717x_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct saa717x_state *decoder = to_state(sd); - int audio_mode; - char *mes[4] = { - "MONO", "STEREO", "LANG1", "LANG2/SAP" - }; - - audio_mode = TUNER_AUDIO_STEREO; - - switch (vt->audmode) { - case V4L2_TUNER_MODE_MONO: - audio_mode = TUNER_AUDIO_MONO; - break; - case V4L2_TUNER_MODE_STEREO: - audio_mode = TUNER_AUDIO_STEREO; - break; - case V4L2_TUNER_MODE_LANG2: - audio_mode = TUNER_AUDIO_LANG2; - break; - case V4L2_TUNER_MODE_LANG1: - audio_mode = TUNER_AUDIO_LANG1; - break; - } - - v4l2_dbg(1, debug, sd, "change audio mode to %s\n", - mes[audio_mode]); - decoder->tuner_audio_mode = audio_mode; - /* The registers are not changed here. */ - /* See DECODER_ENABLE_OUTPUT section. */ - set_audio_mode(sd, decoder->tuner_audio_mode); - return 0; -} - -static int saa717x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct saa717x_state *decoder = to_state(sd); - int dual_f, stereo_f; - - if (decoder->radio) - return 0; - get_inf_dev_status(sd, &dual_f, &stereo_f); - - v4l2_dbg(1, debug, sd, "DETECT==st:%d dual:%d\n", - stereo_f, dual_f); - - /* mono */ - if ((dual_f == 0) && (stereo_f == 0)) { - vt->rxsubchans = V4L2_TUNER_SUB_MONO; - v4l2_dbg(1, debug, sd, "DETECT==MONO\n"); - } - - /* stereo */ - if (stereo_f == 1) { - if (vt->audmode == V4L2_TUNER_MODE_STEREO || - vt->audmode == V4L2_TUNER_MODE_LANG1) { - vt->rxsubchans = V4L2_TUNER_SUB_STEREO; - v4l2_dbg(1, debug, sd, "DETECT==ST(ST)\n"); - } else { - vt->rxsubchans = V4L2_TUNER_SUB_MONO; - v4l2_dbg(1, debug, sd, "DETECT==ST(MONO)\n"); - } - } - - /* dual */ - if (dual_f == 1) { - if (vt->audmode == V4L2_TUNER_MODE_LANG2) { - vt->rxsubchans = V4L2_TUNER_SUB_LANG2 | V4L2_TUNER_SUB_MONO; - v4l2_dbg(1, debug, sd, "DETECT==DUAL1\n"); - } else { - vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_MONO; - v4l2_dbg(1, debug, sd, "DETECT==DUAL2\n"); - } - } - return 0; -} - -static int saa717x_log_status(struct v4l2_subdev *sd) -{ - struct saa717x_state *state = to_state(sd); - - v4l2_ctrl_handler_log_status(&state->hdl, sd->name); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_ctrl_ops saa717x_ctrl_ops = { - .s_ctrl = saa717x_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops saa717x_core_ops = { -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = saa717x_g_register, - .s_register = saa717x_s_register, -#endif - .s_std = saa717x_s_std, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, - .log_status = saa717x_log_status, -}; - -static const struct v4l2_subdev_tuner_ops saa717x_tuner_ops = { - .g_tuner = saa717x_g_tuner, - .s_tuner = saa717x_s_tuner, - .s_radio = saa717x_s_radio, -}; - -static const struct v4l2_subdev_video_ops saa717x_video_ops = { - .s_routing = saa717x_s_video_routing, - .s_mbus_fmt = saa717x_s_mbus_fmt, - .s_stream = saa717x_s_stream, -}; - -static const struct v4l2_subdev_audio_ops saa717x_audio_ops = { - .s_routing = saa717x_s_audio_routing, -}; - -static const struct v4l2_subdev_ops saa717x_ops = { - .core = &saa717x_core_ops, - .tuner = &saa717x_tuner_ops, - .audio = &saa717x_audio_ops, - .video = &saa717x_video_ops, -}; - -/* ----------------------------------------------------------------------- */ - - -/* i2c implementation */ - -/* ----------------------------------------------------------------------- */ -static int saa717x_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct saa717x_state *decoder; - struct v4l2_ctrl_handler *hdl; - struct v4l2_subdev *sd; - u8 id = 0; - char *p = ""; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - decoder = kzalloc(sizeof(struct saa717x_state), GFP_KERNEL); - if (decoder == NULL) - return -ENOMEM; - - sd = &decoder->sd; - v4l2_i2c_subdev_init(sd, client, &saa717x_ops); - - if (saa717x_write(sd, 0x5a4, 0xfe) && - saa717x_write(sd, 0x5a5, 0x0f) && - saa717x_write(sd, 0x5a6, 0x00) && - saa717x_write(sd, 0x5a7, 0x01)) - id = saa717x_read(sd, 0x5a0); - if (id != 0xc2 && id != 0x32 && id != 0xf2 && id != 0x6c) { - v4l2_dbg(1, debug, sd, "saa717x not found (id=%02x)\n", id); - kfree(decoder); - return -ENODEV; - } - if (id == 0xc2) - p = "saa7173"; - else if (id == 0x32) - p = "saa7174A"; - else if (id == 0x6c) - p = "saa7174HL"; - else - p = "saa7171"; - v4l2_info(sd, "%s found @ 0x%x (%s)\n", p, - client->addr << 1, client->adapter->name); - - hdl = &decoder->hdl; - v4l2_ctrl_handler_init(hdl, 9); - /* add in ascending ID order */ - v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); - v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, 68); - v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, - V4L2_CID_SATURATION, 0, 255, 1, 64); - v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, - V4L2_CID_HUE, -128, 127, 1, 0); - v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, - V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 42000); - v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, - V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768); - v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, - V4L2_CID_AUDIO_BASS, -16, 15, 1, 0); - v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, - V4L2_CID_AUDIO_TREBLE, -16, 15, 1, 0); - v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, - V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); - sd->ctrl_handler = hdl; - if (hdl->error) { - int err = hdl->error; - - v4l2_ctrl_handler_free(hdl); - kfree(decoder); - return err; - } - - decoder->std = V4L2_STD_NTSC; - decoder->input = -1; - decoder->enable = 1; - - /* FIXME!! */ - decoder->playback = 0; /* initially capture mode used */ - decoder->audio = 1; /* DECODER_AUDIO_48_KHZ */ - - decoder->audio_input = 2; /* FIXME!! */ - - decoder->tuner_audio_mode = TUNER_AUDIO_STEREO; - /* set volume, bass and treble */ - decoder->audio_main_vol_l = 6; - decoder->audio_main_vol_r = 6; - - v4l2_dbg(1, debug, sd, "writing init values\n"); - - /* FIXME!! */ - saa717x_write_regs(sd, reg_init_initialize); - - v4l2_ctrl_handler_setup(hdl); - - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(2*HZ); - return 0; -} - -static int saa717x_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(sd->ctrl_handler); - kfree(to_state(sd)); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id saa717x_id[] = { - { "saa717x", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, saa717x_id); - -static struct i2c_driver saa717x_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "saa717x", - }, - .probe = saa717x_probe, - .remove = saa717x_remove, - .id_table = saa717x_id, -}; - -module_i2c_driver(saa717x_driver); diff --git a/drivers/media/video/saa7185.c b/drivers/media/video/saa7185.c deleted file mode 100644 index 2c6b65c76e2..00000000000 --- a/drivers/media/video/saa7185.c +++ /dev/null @@ -1,377 +0,0 @@ -/* - * saa7185 - Philips SAA7185B video encoder driver version 0.0.3 - * - * Copyright (C) 1998 Dave Perks - * - * Slight changes for video timing and attachment output by - * Wolfgang Scherr - * - * Changes by Ronald Bultje - * - moved over to linux>=2.4.x i2c protocol (1/1/2003) - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("Philips SAA7185 video encoder driver"); -MODULE_AUTHOR("Dave Perks"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - - -/* ----------------------------------------------------------------------- */ - -struct saa7185 { - struct v4l2_subdev sd; - unsigned char reg[128]; - - v4l2_std_id norm; -}; - -static inline struct saa7185 *to_saa7185(struct v4l2_subdev *sd) -{ - return container_of(sd, struct saa7185, sd); -} - -/* ----------------------------------------------------------------------- */ - -static inline int saa7185_read(struct v4l2_subdev *sd) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_read_byte(client); -} - -static int saa7185_write(struct v4l2_subdev *sd, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct saa7185 *encoder = to_saa7185(sd); - - v4l2_dbg(1, debug, sd, "%02x set to %02x\n", reg, value); - encoder->reg[reg] = value; - return i2c_smbus_write_byte_data(client, reg, value); -} - -static int saa7185_write_block(struct v4l2_subdev *sd, - const u8 *data, unsigned int len) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct saa7185 *encoder = to_saa7185(sd); - int ret = -1; - u8 reg; - - /* the adv7175 has an autoincrement function, use it if - * the adapter understands raw I2C */ - if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - /* do raw I2C, not smbus compatible */ - u8 block_data[32]; - int block_len; - - while (len >= 2) { - block_len = 0; - block_data[block_len++] = reg = data[0]; - do { - block_data[block_len++] = - encoder->reg[reg++] = data[1]; - len -= 2; - data += 2; - } while (len >= 2 && data[0] == reg && block_len < 32); - ret = i2c_master_send(client, block_data, block_len); - if (ret < 0) - break; - } - } else { - /* do some slow I2C emulation kind of thing */ - while (len >= 2) { - reg = *data++; - ret = saa7185_write(sd, reg, *data++); - if (ret < 0) - break; - len -= 2; - } - } - - return ret; -} - -/* ----------------------------------------------------------------------- */ - -static const unsigned char init_common[] = { - 0x3a, 0x0f, /* CBENB=0, V656=0, VY2C=1, - * YUV2C=1, MY2C=1, MUV2C=1 */ - - 0x42, 0x6b, /* OVLY0=107 */ - 0x43, 0x00, /* OVLU0=0 white */ - 0x44, 0x00, /* OVLV0=0 */ - 0x45, 0x22, /* OVLY1=34 */ - 0x46, 0xac, /* OVLU1=172 yellow */ - 0x47, 0x0e, /* OVLV1=14 */ - 0x48, 0x03, /* OVLY2=3 */ - 0x49, 0x1d, /* OVLU2=29 cyan */ - 0x4a, 0xac, /* OVLV2=172 */ - 0x4b, 0xf0, /* OVLY3=240 */ - 0x4c, 0xc8, /* OVLU3=200 green */ - 0x4d, 0xb9, /* OVLV3=185 */ - 0x4e, 0xd4, /* OVLY4=212 */ - 0x4f, 0x38, /* OVLU4=56 magenta */ - 0x50, 0x47, /* OVLV4=71 */ - 0x51, 0xc1, /* OVLY5=193 */ - 0x52, 0xe3, /* OVLU5=227 red */ - 0x53, 0x54, /* OVLV5=84 */ - 0x54, 0xa3, /* OVLY6=163 */ - 0x55, 0x54, /* OVLU6=84 blue */ - 0x56, 0xf2, /* OVLV6=242 */ - 0x57, 0x90, /* OVLY7=144 */ - 0x58, 0x00, /* OVLU7=0 black */ - 0x59, 0x00, /* OVLV7=0 */ - - 0x5a, 0x00, /* CHPS=0 */ - 0x5b, 0x76, /* GAINU=118 */ - 0x5c, 0xa5, /* GAINV=165 */ - 0x5d, 0x3c, /* BLCKL=60 */ - 0x5e, 0x3a, /* BLNNL=58 */ - 0x5f, 0x3a, /* CCRS=0, BLNVB=58 */ - 0x60, 0x00, /* NULL */ - - /* 0x61 - 0x66 set according to norm */ - - 0x67, 0x00, /* 0 : caption 1st byte odd field */ - 0x68, 0x00, /* 0 : caption 2nd byte odd field */ - 0x69, 0x00, /* 0 : caption 1st byte even field */ - 0x6a, 0x00, /* 0 : caption 2nd byte even field */ - - 0x6b, 0x91, /* MODIN=2, PCREF=0, SCCLN=17 */ - 0x6c, 0x20, /* SRCV1=0, TRCV2=1, ORCV1=0, PRCV1=0, - * CBLF=0, ORCV2=0, PRCV2=0 */ - 0x6d, 0x00, /* SRCM1=0, CCEN=0 */ - - 0x6e, 0x0e, /* HTRIG=0x005, approx. centered, at - * least for PAL */ - 0x6f, 0x00, /* HTRIG upper bits */ - 0x70, 0x20, /* PHRES=0, SBLN=1, VTRIG=0 */ - - /* The following should not be needed */ - - 0x71, 0x15, /* BMRQ=0x115 */ - 0x72, 0x90, /* EMRQ=0x690 */ - 0x73, 0x61, /* EMRQ=0x690, BMRQ=0x115 */ - 0x74, 0x00, /* NULL */ - 0x75, 0x00, /* NULL */ - 0x76, 0x00, /* NULL */ - 0x77, 0x15, /* BRCV=0x115 */ - 0x78, 0x90, /* ERCV=0x690 */ - 0x79, 0x61, /* ERCV=0x690, BRCV=0x115 */ - - /* Field length controls */ - - 0x7a, 0x70, /* FLC=0 */ - - /* The following should not be needed if SBLN = 1 */ - - 0x7b, 0x16, /* FAL=22 */ - 0x7c, 0x35, /* LAL=244 */ - 0x7d, 0x20, /* LAL=244, FAL=22 */ -}; - -static const unsigned char init_pal[] = { - 0x61, 0x1e, /* FISE=0, PAL=1, SCBW=1, RTCE=1, - * YGS=1, INPI=0, DOWN=0 */ - 0x62, 0xc8, /* DECTYP=1, BSTA=72 */ - 0x63, 0xcb, /* FSC0 */ - 0x64, 0x8a, /* FSC1 */ - 0x65, 0x09, /* FSC2 */ - 0x66, 0x2a, /* FSC3 */ -}; - -static const unsigned char init_ntsc[] = { - 0x61, 0x1d, /* FISE=1, PAL=0, SCBW=1, RTCE=1, - * YGS=1, INPI=0, DOWN=0 */ - 0x62, 0xe6, /* DECTYP=1, BSTA=102 */ - 0x63, 0x1f, /* FSC0 */ - 0x64, 0x7c, /* FSC1 */ - 0x65, 0xf0, /* FSC2 */ - 0x66, 0x21, /* FSC3 */ -}; - - -static int saa7185_init(struct v4l2_subdev *sd, u32 val) -{ - struct saa7185 *encoder = to_saa7185(sd); - - saa7185_write_block(sd, init_common, sizeof(init_common)); - if (encoder->norm & V4L2_STD_NTSC) - saa7185_write_block(sd, init_ntsc, sizeof(init_ntsc)); - else - saa7185_write_block(sd, init_pal, sizeof(init_pal)); - return 0; -} - -static int saa7185_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct saa7185 *encoder = to_saa7185(sd); - - if (std & V4L2_STD_NTSC) - saa7185_write_block(sd, init_ntsc, sizeof(init_ntsc)); - else if (std & V4L2_STD_PAL) - saa7185_write_block(sd, init_pal, sizeof(init_pal)); - else - return -EINVAL; - encoder->norm = std; - return 0; -} - -static int saa7185_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct saa7185 *encoder = to_saa7185(sd); - - /* RJ: input = 0: input is from SA7111 - input = 1: input is from ZR36060 */ - - switch (input) { - case 0: - /* turn off colorbar */ - saa7185_write(sd, 0x3a, 0x0f); - /* Switch RTCE to 1 */ - saa7185_write(sd, 0x61, (encoder->reg[0x61] & 0xf7) | 0x08); - saa7185_write(sd, 0x6e, 0x01); - break; - - case 1: - /* turn off colorbar */ - saa7185_write(sd, 0x3a, 0x0f); - /* Switch RTCE to 0 */ - saa7185_write(sd, 0x61, (encoder->reg[0x61] & 0xf7) | 0x00); - /* SW: a slight sync problem... */ - saa7185_write(sd, 0x6e, 0x00); - break; - - case 2: - /* turn on colorbar */ - saa7185_write(sd, 0x3a, 0x8f); - /* Switch RTCE to 0 */ - saa7185_write(sd, 0x61, (encoder->reg[0x61] & 0xf7) | 0x08); - /* SW: a slight sync problem... */ - saa7185_write(sd, 0x6e, 0x01); - break; - - default: - return -EINVAL; - } - return 0; -} - -static int saa7185_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7185, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops saa7185_core_ops = { - .g_chip_ident = saa7185_g_chip_ident, - .init = saa7185_init, -}; - -static const struct v4l2_subdev_video_ops saa7185_video_ops = { - .s_std_output = saa7185_s_std_output, - .s_routing = saa7185_s_routing, -}; - -static const struct v4l2_subdev_ops saa7185_ops = { - .core = &saa7185_core_ops, - .video = &saa7185_video_ops, -}; - - -/* ----------------------------------------------------------------------- */ - -static int saa7185_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - int i; - struct saa7185 *encoder; - struct v4l2_subdev *sd; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - encoder = kzalloc(sizeof(struct saa7185), GFP_KERNEL); - if (encoder == NULL) - return -ENOMEM; - encoder->norm = V4L2_STD_NTSC; - sd = &encoder->sd; - v4l2_i2c_subdev_init(sd, client, &saa7185_ops); - - i = saa7185_write_block(sd, init_common, sizeof(init_common)); - if (i >= 0) - i = saa7185_write_block(sd, init_ntsc, sizeof(init_ntsc)); - if (i < 0) - v4l2_dbg(1, debug, sd, "init error %d\n", i); - else - v4l2_dbg(1, debug, sd, "revision 0x%x\n", - saa7185_read(sd) >> 5); - return 0; -} - -static int saa7185_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct saa7185 *encoder = to_saa7185(sd); - - v4l2_device_unregister_subdev(sd); - /* SW: output off is active */ - saa7185_write(sd, 0x61, (encoder->reg[0x61]) | 0x40); - kfree(encoder); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id saa7185_id[] = { - { "saa7185", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, saa7185_id); - -static struct i2c_driver saa7185_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "saa7185", - }, - .probe = saa7185_probe, - .remove = saa7185_remove, - .id_table = saa7185_id, -}; - -module_i2c_driver(saa7185_driver); diff --git a/drivers/media/video/saa7191.c b/drivers/media/video/saa7191.c deleted file mode 100644 index d7d1670e0ca..00000000000 --- a/drivers/media/video/saa7191.c +++ /dev/null @@ -1,659 +0,0 @@ -/* - * saa7191.c - Philips SAA7191 video decoder driver - * - * Copyright (C) 2003 Ladislav Michl - * Copyright (C) 2004,2005 Mikael Nousiainen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "saa7191.h" - -#define SAA7191_MODULE_VERSION "0.0.5" - -MODULE_DESCRIPTION("Philips SAA7191 video decoder driver"); -MODULE_VERSION(SAA7191_MODULE_VERSION); -MODULE_AUTHOR("Mikael Nousiainen "); -MODULE_LICENSE("GPL"); - - -// #define SAA7191_DEBUG - -#ifdef SAA7191_DEBUG -#define dprintk(x...) printk("SAA7191: " x); -#else -#define dprintk(x...) -#endif - -#define SAA7191_SYNC_COUNT 30 -#define SAA7191_SYNC_DELAY 100 /* milliseconds */ - -struct saa7191 { - struct v4l2_subdev sd; - - /* the register values are stored here as the actual - * I2C-registers are write-only */ - u8 reg[25]; - - int input; - v4l2_std_id norm; -}; - -static inline struct saa7191 *to_saa7191(struct v4l2_subdev *sd) -{ - return container_of(sd, struct saa7191, sd); -} - -static const u8 initseq[] = { - 0, /* Subaddress */ - - 0x50, /* (0x50) SAA7191_REG_IDEL */ - - /* 50 Hz signal timing */ - 0x30, /* (0x30) SAA7191_REG_HSYB */ - 0x00, /* (0x00) SAA7191_REG_HSYS */ - 0xe8, /* (0xe8) SAA7191_REG_HCLB */ - 0xb6, /* (0xb6) SAA7191_REG_HCLS */ - 0xf4, /* (0xf4) SAA7191_REG_HPHI */ - - /* control */ - SAA7191_LUMA_APER_1, /* (0x01) SAA7191_REG_LUMA - CVBS mode */ - 0x00, /* (0x00) SAA7191_REG_HUEC */ - 0xf8, /* (0xf8) SAA7191_REG_CKTQ */ - 0xf8, /* (0xf8) SAA7191_REG_CKTS */ - 0x90, /* (0x90) SAA7191_REG_PLSE */ - 0x90, /* (0x90) SAA7191_REG_SESE */ - 0x00, /* (0x00) SAA7191_REG_GAIN */ - SAA7191_STDC_NFEN | SAA7191_STDC_HRMV, /* (0x0c) SAA7191_REG_STDC - * - not SECAM, - * slow time constant */ - SAA7191_IOCK_OEDC | SAA7191_IOCK_OEHS | SAA7191_IOCK_OEVS - | SAA7191_IOCK_OEDY, /* (0x78) SAA7191_REG_IOCK - * - chroma from CVBS, GPSW1 & 2 off */ - SAA7191_CTL3_AUFD | SAA7191_CTL3_SCEN | SAA7191_CTL3_OFTS - | SAA7191_CTL3_YDEL0, /* (0x99) SAA7191_REG_CTL3 - * - automatic field detection */ - 0x00, /* (0x00) SAA7191_REG_CTL4 */ - 0x2c, /* (0x2c) SAA7191_REG_CHCV - PAL nominal value */ - 0x00, /* unused */ - 0x00, /* unused */ - - /* 60 Hz signal timing */ - 0x34, /* (0x34) SAA7191_REG_HS6B */ - 0x0a, /* (0x0a) SAA7191_REG_HS6S */ - 0xf4, /* (0xf4) SAA7191_REG_HC6B */ - 0xce, /* (0xce) SAA7191_REG_HC6S */ - 0xf4, /* (0xf4) SAA7191_REG_HP6I */ -}; - -/* SAA7191 register handling */ - -static u8 saa7191_read_reg(struct v4l2_subdev *sd, u8 reg) -{ - return to_saa7191(sd)->reg[reg]; -} - -static int saa7191_read_status(struct v4l2_subdev *sd, u8 *value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - ret = i2c_master_recv(client, value, 1); - if (ret < 0) { - printk(KERN_ERR "SAA7191: saa7191_read_status(): read failed\n"); - return ret; - } - - return 0; -} - - -static int saa7191_write_reg(struct v4l2_subdev *sd, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - to_saa7191(sd)->reg[reg] = value; - return i2c_smbus_write_byte_data(client, reg, value); -} - -/* the first byte of data must be the first subaddress number (register) */ -static int saa7191_write_block(struct v4l2_subdev *sd, - u8 length, const u8 *data) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct saa7191 *decoder = to_saa7191(sd); - int i; - int ret; - - for (i = 0; i < (length - 1); i++) { - decoder->reg[data[0] + i] = data[i + 1]; - } - - ret = i2c_master_send(client, data, length); - if (ret < 0) { - printk(KERN_ERR "SAA7191: saa7191_write_block(): " - "write failed\n"); - return ret; - } - - return 0; -} - -/* Helper functions */ - -static int saa7191_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct saa7191 *decoder = to_saa7191(sd); - u8 luma = saa7191_read_reg(sd, SAA7191_REG_LUMA); - u8 iock = saa7191_read_reg(sd, SAA7191_REG_IOCK); - int err; - - switch (input) { - case SAA7191_INPUT_COMPOSITE: /* Set Composite input */ - iock &= ~(SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW1 - | SAA7191_IOCK_GPSW2); - /* Chrominance trap active */ - luma &= ~SAA7191_LUMA_BYPS; - break; - case SAA7191_INPUT_SVIDEO: /* Set S-Video input */ - iock |= SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW2; - /* Chrominance trap bypassed */ - luma |= SAA7191_LUMA_BYPS; - break; - default: - return -EINVAL; - } - - err = saa7191_write_reg(sd, SAA7191_REG_LUMA, luma); - if (err) - return -EIO; - err = saa7191_write_reg(sd, SAA7191_REG_IOCK, iock); - if (err) - return -EIO; - - decoder->input = input; - - return 0; -} - -static int saa7191_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) -{ - struct saa7191 *decoder = to_saa7191(sd); - u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC); - u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3); - u8 chcv = saa7191_read_reg(sd, SAA7191_REG_CHCV); - int err; - - if (norm & V4L2_STD_PAL) { - stdc &= ~SAA7191_STDC_SECS; - ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL); - chcv = SAA7191_CHCV_PAL; - } else if (norm & V4L2_STD_NTSC) { - stdc &= ~SAA7191_STDC_SECS; - ctl3 &= ~SAA7191_CTL3_AUFD; - ctl3 |= SAA7191_CTL3_FSEL; - chcv = SAA7191_CHCV_NTSC; - } else if (norm & V4L2_STD_SECAM) { - stdc |= SAA7191_STDC_SECS; - ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL); - chcv = SAA7191_CHCV_PAL; - } else { - return -EINVAL; - } - - err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3); - if (err) - return -EIO; - err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc); - if (err) - return -EIO; - err = saa7191_write_reg(sd, SAA7191_REG_CHCV, chcv); - if (err) - return -EIO; - - decoder->norm = norm; - - dprintk("ctl3: %02x stdc: %02x chcv: %02x\n", ctl3, - stdc, chcv); - dprintk("norm: %llx\n", norm); - - return 0; -} - -static int saa7191_wait_for_signal(struct v4l2_subdev *sd, u8 *status) -{ - int i = 0; - - dprintk("Checking for signal...\n"); - - for (i = 0; i < SAA7191_SYNC_COUNT; i++) { - if (saa7191_read_status(sd, status)) - return -EIO; - - if (((*status) & SAA7191_STATUS_HLCK) == 0) { - dprintk("Signal found\n"); - return 0; - } - - msleep(SAA7191_SYNC_DELAY); - } - - dprintk("No signal\n"); - - return -EBUSY; -} - -static int saa7191_querystd(struct v4l2_subdev *sd, v4l2_std_id *norm) -{ - struct saa7191 *decoder = to_saa7191(sd); - u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC); - u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3); - u8 status; - v4l2_std_id old_norm = decoder->norm; - int err = 0; - - dprintk("SAA7191 extended signal auto-detection...\n"); - - *norm = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM; - stdc &= ~SAA7191_STDC_SECS; - ctl3 &= ~(SAA7191_CTL3_FSEL); - - err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc); - if (err) { - err = -EIO; - goto out; - } - err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3); - if (err) { - err = -EIO; - goto out; - } - - ctl3 |= SAA7191_CTL3_AUFD; - err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3); - if (err) { - err = -EIO; - goto out; - } - - msleep(SAA7191_SYNC_DELAY); - - err = saa7191_wait_for_signal(sd, &status); - if (err) - goto out; - - if (status & SAA7191_STATUS_FIDT) { - /* 60Hz signal -> NTSC */ - dprintk("60Hz signal: NTSC\n"); - *norm = V4L2_STD_NTSC; - return 0; - } - - /* 50Hz signal */ - dprintk("50Hz signal: Trying PAL...\n"); - - /* try PAL first */ - err = saa7191_s_std(sd, V4L2_STD_PAL); - if (err) - goto out; - - msleep(SAA7191_SYNC_DELAY); - - err = saa7191_wait_for_signal(sd, &status); - if (err) - goto out; - - /* not 50Hz ? */ - if (status & SAA7191_STATUS_FIDT) { - dprintk("No 50Hz signal\n"); - saa7191_s_std(sd, old_norm); - return -EAGAIN; - } - - if (status & SAA7191_STATUS_CODE) { - dprintk("PAL\n"); - *norm = V4L2_STD_PAL; - return saa7191_s_std(sd, old_norm); - } - - dprintk("No color detected with PAL - Trying SECAM...\n"); - - /* no color detected ? -> try SECAM */ - err = saa7191_s_std(sd, V4L2_STD_SECAM); - if (err) - goto out; - - msleep(SAA7191_SYNC_DELAY); - - err = saa7191_wait_for_signal(sd, &status); - if (err) - goto out; - - /* not 50Hz ? */ - if (status & SAA7191_STATUS_FIDT) { - dprintk("No 50Hz signal\n"); - err = -EAGAIN; - goto out; - } - - if (status & SAA7191_STATUS_CODE) { - /* Color detected -> SECAM */ - dprintk("SECAM\n"); - *norm = V4L2_STD_SECAM; - return saa7191_s_std(sd, old_norm); - } - - dprintk("No color detected with SECAM - Going back to PAL.\n"); - -out: - return saa7191_s_std(sd, old_norm); -} - -static int saa7191_autodetect_norm(struct v4l2_subdev *sd) -{ - u8 status; - - dprintk("SAA7191 signal auto-detection...\n"); - - dprintk("Reading status...\n"); - - if (saa7191_read_status(sd, &status)) - return -EIO; - - dprintk("Checking for signal...\n"); - - /* no signal ? */ - if (status & SAA7191_STATUS_HLCK) { - dprintk("No signal\n"); - return -EBUSY; - } - - dprintk("Signal found\n"); - - if (status & SAA7191_STATUS_FIDT) { - /* 60hz signal -> NTSC */ - dprintk("NTSC\n"); - return saa7191_s_std(sd, V4L2_STD_NTSC); - } else { - /* 50hz signal -> PAL */ - dprintk("PAL\n"); - return saa7191_s_std(sd, V4L2_STD_PAL); - } -} - -static int saa7191_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - u8 reg; - int ret = 0; - - switch (ctrl->id) { - case SAA7191_CONTROL_BANDPASS: - case SAA7191_CONTROL_BANDPASS_WEIGHT: - case SAA7191_CONTROL_CORING: - reg = saa7191_read_reg(sd, SAA7191_REG_LUMA); - switch (ctrl->id) { - case SAA7191_CONTROL_BANDPASS: - ctrl->value = ((s32)reg & SAA7191_LUMA_BPSS_MASK) - >> SAA7191_LUMA_BPSS_SHIFT; - break; - case SAA7191_CONTROL_BANDPASS_WEIGHT: - ctrl->value = ((s32)reg & SAA7191_LUMA_APER_MASK) - >> SAA7191_LUMA_APER_SHIFT; - break; - case SAA7191_CONTROL_CORING: - ctrl->value = ((s32)reg & SAA7191_LUMA_CORI_MASK) - >> SAA7191_LUMA_CORI_SHIFT; - break; - } - break; - case SAA7191_CONTROL_FORCE_COLOUR: - case SAA7191_CONTROL_CHROMA_GAIN: - reg = saa7191_read_reg(sd, SAA7191_REG_GAIN); - if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR) - ctrl->value = ((s32)reg & SAA7191_GAIN_COLO) ? 1 : 0; - else - ctrl->value = ((s32)reg & SAA7191_GAIN_LFIS_MASK) - >> SAA7191_GAIN_LFIS_SHIFT; - break; - case V4L2_CID_HUE: - reg = saa7191_read_reg(sd, SAA7191_REG_HUEC); - if (reg < 0x80) - reg += 0x80; - else - reg -= 0x80; - ctrl->value = (s32)reg; - break; - case SAA7191_CONTROL_VTRC: - reg = saa7191_read_reg(sd, SAA7191_REG_STDC); - ctrl->value = ((s32)reg & SAA7191_STDC_VTRC) ? 1 : 0; - break; - case SAA7191_CONTROL_LUMA_DELAY: - reg = saa7191_read_reg(sd, SAA7191_REG_CTL3); - ctrl->value = ((s32)reg & SAA7191_CTL3_YDEL_MASK) - >> SAA7191_CTL3_YDEL_SHIFT; - if (ctrl->value >= 4) - ctrl->value -= 8; - break; - case SAA7191_CONTROL_VNR: - reg = saa7191_read_reg(sd, SAA7191_REG_CTL4); - ctrl->value = ((s32)reg & SAA7191_CTL4_VNOI_MASK) - >> SAA7191_CTL4_VNOI_SHIFT; - break; - default: - ret = -EINVAL; - } - - return ret; -} - -static int saa7191_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - u8 reg; - int ret = 0; - - switch (ctrl->id) { - case SAA7191_CONTROL_BANDPASS: - case SAA7191_CONTROL_BANDPASS_WEIGHT: - case SAA7191_CONTROL_CORING: - reg = saa7191_read_reg(sd, SAA7191_REG_LUMA); - switch (ctrl->id) { - case SAA7191_CONTROL_BANDPASS: - reg &= ~SAA7191_LUMA_BPSS_MASK; - reg |= (ctrl->value << SAA7191_LUMA_BPSS_SHIFT) - & SAA7191_LUMA_BPSS_MASK; - break; - case SAA7191_CONTROL_BANDPASS_WEIGHT: - reg &= ~SAA7191_LUMA_APER_MASK; - reg |= (ctrl->value << SAA7191_LUMA_APER_SHIFT) - & SAA7191_LUMA_APER_MASK; - break; - case SAA7191_CONTROL_CORING: - reg &= ~SAA7191_LUMA_CORI_MASK; - reg |= (ctrl->value << SAA7191_LUMA_CORI_SHIFT) - & SAA7191_LUMA_CORI_MASK; - break; - } - ret = saa7191_write_reg(sd, SAA7191_REG_LUMA, reg); - break; - case SAA7191_CONTROL_FORCE_COLOUR: - case SAA7191_CONTROL_CHROMA_GAIN: - reg = saa7191_read_reg(sd, SAA7191_REG_GAIN); - if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR) { - if (ctrl->value) - reg |= SAA7191_GAIN_COLO; - else - reg &= ~SAA7191_GAIN_COLO; - } else { - reg &= ~SAA7191_GAIN_LFIS_MASK; - reg |= (ctrl->value << SAA7191_GAIN_LFIS_SHIFT) - & SAA7191_GAIN_LFIS_MASK; - } - ret = saa7191_write_reg(sd, SAA7191_REG_GAIN, reg); - break; - case V4L2_CID_HUE: - reg = ctrl->value & 0xff; - if (reg < 0x80) - reg += 0x80; - else - reg -= 0x80; - ret = saa7191_write_reg(sd, SAA7191_REG_HUEC, reg); - break; - case SAA7191_CONTROL_VTRC: - reg = saa7191_read_reg(sd, SAA7191_REG_STDC); - if (ctrl->value) - reg |= SAA7191_STDC_VTRC; - else - reg &= ~SAA7191_STDC_VTRC; - ret = saa7191_write_reg(sd, SAA7191_REG_STDC, reg); - break; - case SAA7191_CONTROL_LUMA_DELAY: { - s32 value = ctrl->value; - if (value < 0) - value += 8; - reg = saa7191_read_reg(sd, SAA7191_REG_CTL3); - reg &= ~SAA7191_CTL3_YDEL_MASK; - reg |= (value << SAA7191_CTL3_YDEL_SHIFT) - & SAA7191_CTL3_YDEL_MASK; - ret = saa7191_write_reg(sd, SAA7191_REG_CTL3, reg); - break; - } - case SAA7191_CONTROL_VNR: - reg = saa7191_read_reg(sd, SAA7191_REG_CTL4); - reg &= ~SAA7191_CTL4_VNOI_MASK; - reg |= (ctrl->value << SAA7191_CTL4_VNOI_SHIFT) - & SAA7191_CTL4_VNOI_MASK; - ret = saa7191_write_reg(sd, SAA7191_REG_CTL4, reg); - break; - default: - ret = -EINVAL; - } - - return ret; -} - -/* I2C-interface */ - -static int saa7191_g_input_status(struct v4l2_subdev *sd, u32 *status) -{ - u8 status_reg; - int res = V4L2_IN_ST_NO_SIGNAL; - - if (saa7191_read_status(sd, &status_reg)) - return -EIO; - if ((status_reg & SAA7191_STATUS_HLCK) == 0) - res = 0; - if (!(status_reg & SAA7191_STATUS_CODE)) - res |= V4L2_IN_ST_NO_COLOR; - *status = res; - return 0; -} - - -static int saa7191_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7191, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops saa7191_core_ops = { - .g_chip_ident = saa7191_g_chip_ident, - .g_ctrl = saa7191_g_ctrl, - .s_ctrl = saa7191_s_ctrl, - .s_std = saa7191_s_std, -}; - -static const struct v4l2_subdev_video_ops saa7191_video_ops = { - .s_routing = saa7191_s_routing, - .querystd = saa7191_querystd, - .g_input_status = saa7191_g_input_status, -}; - -static const struct v4l2_subdev_ops saa7191_ops = { - .core = &saa7191_core_ops, - .video = &saa7191_video_ops, -}; - -static int saa7191_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - int err = 0; - struct saa7191 *decoder; - struct v4l2_subdev *sd; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - decoder = kzalloc(sizeof(*decoder), GFP_KERNEL); - if (!decoder) - return -ENOMEM; - - sd = &decoder->sd; - v4l2_i2c_subdev_init(sd, client, &saa7191_ops); - - err = saa7191_write_block(sd, sizeof(initseq), initseq); - if (err) { - printk(KERN_ERR "SAA7191 initialization failed\n"); - kfree(decoder); - return err; - } - - printk(KERN_INFO "SAA7191 initialized\n"); - - decoder->input = SAA7191_INPUT_COMPOSITE; - decoder->norm = V4L2_STD_PAL; - - err = saa7191_autodetect_norm(sd); - if (err && (err != -EBUSY)) - printk(KERN_ERR "SAA7191: Signal auto-detection failed\n"); - - return 0; -} - -static int saa7191_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - kfree(to_saa7191(sd)); - return 0; -} - -static const struct i2c_device_id saa7191_id[] = { - { "saa7191", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, saa7191_id); - -static struct i2c_driver saa7191_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "saa7191", - }, - .probe = saa7191_probe, - .remove = saa7191_remove, - .id_table = saa7191_id, -}; - -module_i2c_driver(saa7191_driver); diff --git a/drivers/media/video/saa7191.h b/drivers/media/video/saa7191.h deleted file mode 100644 index 803c74d6066..00000000000 --- a/drivers/media/video/saa7191.h +++ /dev/null @@ -1,245 +0,0 @@ -/* - * saa7191.h - Philips SAA7191 video decoder driver - * - * Copyright (C) 2003 Ladislav Michl - * Copyright (C) 2004,2005 Mikael Nousiainen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef _SAA7191_H_ -#define _SAA7191_H_ - -/* Philips SAA7191 DMSD I2C bus address */ -#define SAA7191_ADDR 0x8a - -/* Register subaddresses. */ -#define SAA7191_REG_IDEL 0x00 -#define SAA7191_REG_HSYB 0x01 -#define SAA7191_REG_HSYS 0x02 -#define SAA7191_REG_HCLB 0x03 -#define SAA7191_REG_HCLS 0x04 -#define SAA7191_REG_HPHI 0x05 -#define SAA7191_REG_LUMA 0x06 -#define SAA7191_REG_HUEC 0x07 -#define SAA7191_REG_CKTQ 0x08 /* bits 3-7 */ -#define SAA7191_REG_CKTS 0x09 /* bits 3-7 */ -#define SAA7191_REG_PLSE 0x0a -#define SAA7191_REG_SESE 0x0b -#define SAA7191_REG_GAIN 0x0c -#define SAA7191_REG_STDC 0x0d -#define SAA7191_REG_IOCK 0x0e -#define SAA7191_REG_CTL3 0x0f -#define SAA7191_REG_CTL4 0x10 -#define SAA7191_REG_CHCV 0x11 -#define SAA7191_REG_HS6B 0x14 -#define SAA7191_REG_HS6S 0x15 -#define SAA7191_REG_HC6B 0x16 -#define SAA7191_REG_HC6S 0x17 -#define SAA7191_REG_HP6I 0x18 -#define SAA7191_REG_STATUS 0xff /* not really a subaddress */ - -/* Status Register definitions */ -#define SAA7191_STATUS_CODE 0x01 /* color detected flag */ -#define SAA7191_STATUS_FIDT 0x20 /* signal type 50/60 Hz */ -#define SAA7191_STATUS_HLCK 0x40 /* PLL unlocked(1)/locked(0) */ -#define SAA7191_STATUS_STTC 0x80 /* tv/vtr time constant */ - -/* Luminance Control Register definitions */ -/* input mode select bit: - * 0=CVBS (chrominance trap active), 1=S-Video (trap bypassed) */ -#define SAA7191_LUMA_BYPS 0x80 -/* pre-filter (only when chrominance trap is active) */ -#define SAA7191_LUMA_PREF 0x40 -/* aperture bandpass to select different characteristics with maximums - * (bits 4-5) */ -#define SAA7191_LUMA_BPSS_MASK 0x30 -#define SAA7191_LUMA_BPSS_SHIFT 4 -#define SAA7191_LUMA_BPSS_3 0x30 -#define SAA7191_LUMA_BPSS_2 0x20 -#define SAA7191_LUMA_BPSS_1 0x10 -#define SAA7191_LUMA_BPSS_0 0x00 -/* coring range for high frequency components according to 8-bit luminance - * (bits 2-3) - * 0=coring off, n= (+-)n LSB */ -#define SAA7191_LUMA_CORI_MASK 0x0c -#define SAA7191_LUMA_CORI_SHIFT 2 -#define SAA7191_LUMA_CORI_3 0x0c -#define SAA7191_LUMA_CORI_2 0x08 -#define SAA7191_LUMA_CORI_1 0x04 -#define SAA7191_LUMA_CORI_0 0x00 -/* aperture bandpass filter weights high frequency components of luminance - * signal (bits 0-1) - * 0=factor 0, 1=0.25, 2=0.5, 3=1 */ -#define SAA7191_LUMA_APER_MASK 0x03 -#define SAA7191_LUMA_APER_SHIFT 0 -#define SAA7191_LUMA_APER_3 0x03 -#define SAA7191_LUMA_APER_2 0x02 -#define SAA7191_LUMA_APER_1 0x01 -#define SAA7191_LUMA_APER_0 0x00 - -/* Chrominance Gain Control Settings Register definitions */ -/* colour on: 0=automatic colour-killer enabled, 1=forced colour on */ -#define SAA7191_GAIN_COLO 0x80 -/* chrominance gain control (AGC filter) - * 0=loop filter time constant slow, 1=medium, 2=fast, 3=actual gain */ -#define SAA7191_GAIN_LFIS_MASK 0x60 -#define SAA7191_GAIN_LFIS_SHIFT 5 -#define SAA7191_GAIN_LFIS_3 0x60 -#define SAA7191_GAIN_LFIS_2 0x40 -#define SAA7191_GAIN_LFIS_1 0x20 -#define SAA7191_GAIN_LFIS_0 0x00 - -/* Standard/Mode Control Register definitions */ -/* tv/vtr mode bit: 0=TV mode (slow time constant), - * 1=VTR mode (fast time constant) */ -#define SAA7191_STDC_VTRC 0x80 -/* SAA7191B-specific functions enable (RTCO, ODD and GPSW0 outputs) - * 0=outputs set to high-impedance (circuit equals SAA7191), 1=enabled */ -#define SAA7191_STDC_NFEN 0x08 -/* HREF generation: 0=like SAA7191, 1=HREF is 8xLLC2 clocks earlier */ -#define SAA7191_STDC_HRMV 0x04 -/* general purpose switch 0 - * (not used with VINO afaik) */ -#define SAA7191_STDC_GPSW0 0x02 -/* SECAM mode bit: 0=other standards, 1=SECAM */ -#define SAA7191_STDC_SECS 0x01 - -/* I/O and Clock Control Register definitions */ -/* horizontal clock PLL: 0=PLL closed, - * 1=PLL circuit open and horizontal freq fixed */ -#define SAA7191_IOCK_HPLL 0x80 -/* colour-difference output enable (outputs UV0-UV7) */ -#define SAA7191_IOCK_OEDC 0x40 -/* H-sync output enable */ -#define SAA7191_IOCK_OEHS 0x20 -/* V-sync output enable */ -#define SAA7191_IOCK_OEVS 0x10 -/* luminance output enable (outputs Y0-Y7) */ -#define SAA7191_IOCK_OEDY 0x08 -/* S-VHS bit (chrominance from CVBS or from chrominance input): - * 0=controlled by BYPS-bit, 1=from chrominance input */ -#define SAA7191_IOCK_CHRS 0x04 -/* general purpose switch 2 - * VINO-specific: 0=used with CVBS, 1=used with S-Video */ -#define SAA7191_IOCK_GPSW2 0x02 -/* general purpose switch 1 */ -/* VINO-specific: 0=always, 1=not used!*/ -#define SAA7191_IOCK_GPSW1 0x01 - -/* Miscellaneous Control #1 Register definitions */ -/* automatic field detection (50/60Hz standard) */ -#define SAA7191_CTL3_AUFD 0x80 -/* field select: (if AUFD=0) - * 0=50Hz (625 lines), 1=60Hz (525 lines) */ -#define SAA7191_CTL3_FSEL 0x40 -/* SECAM cross-colour reduction enable */ -#define SAA7191_CTL3_SXCR 0x20 -/* sync and clamping pulse enable (HCL and HSY outputs) */ -#define SAA7191_CTL3_SCEN 0x10 -/* output format: 0=4:1:1, 1=4:2:2 (4:2:2 for VINO) */ -#define SAA7191_CTL3_OFTS 0x08 -/* luminance delay compensation - * 0=0*2/LLC, 1=+1*2/LLC, 2=+2*2/LLC, 3=+3*2/LLC, - * 4=-4*2/LLC, 5=-3*2/LLC, 6=-2*2/LLC, 7=-1*2/LLC - * step size = 2/LLC = 67.8ns for 50Hz, 81.5ns for 60Hz */ -#define SAA7191_CTL3_YDEL_MASK 0x07 -#define SAA7191_CTL3_YDEL_SHIFT 0 -#define SAA7191_CTL3_YDEL2 0x04 -#define SAA7191_CTL3_YDEL1 0x02 -#define SAA7191_CTL3_YDEL0 0x01 - -/* Miscellaneous Control #2 Register definitions */ -/* select HREF position - * 0=normal, HREF is matched to YUV output port, - * 1=HREF is matched to CVBS input port */ -#define SAA7191_CTL4_HRFS 0x04 -/* vertical noise reduction - * 0=normal, 1=searching window, 2=auto-deflection, 3=reduction bypassed */ -#define SAA7191_CTL4_VNOI_MASK 0x03 -#define SAA7191_CTL4_VNOI_SHIFT 0 -#define SAA7191_CTL4_VNOI_3 0x03 -#define SAA7191_CTL4_VNOI_2 0x02 -#define SAA7191_CTL4_VNOI_1 0x01 -#define SAA7191_CTL4_VNOI_0 0x00 - -/* Chrominance Gain Control Register definitions - * - for QAM-modulated input signals, effects output amplitude - * (SECAM gain fixed) - * (nominal values for UV CCIR level) */ -#define SAA7191_CHCV_NTSC 0x2c -#define SAA7191_CHCV_PAL 0x59 - -/* Driver interface definitions */ -#define SAA7191_INPUT_COMPOSITE 0 -#define SAA7191_INPUT_SVIDEO 1 - -#define SAA7191_NORM_PAL 1 -#define SAA7191_NORM_NTSC 2 -#define SAA7191_NORM_SECAM 3 - -struct saa7191_status { - /* 0=no signal, 1=signal detected */ - int signal; - /* 0=50hz (pal) signal, 1=60hz (ntsc) signal */ - int signal_60hz; - /* 0=no color detected, 1=color detected */ - int color; - - /* current SAA7191_INPUT_ */ - int input; - /* current SAA7191_NORM_ */ - int norm; -}; - -#define SAA7191_BANDPASS_MIN 0x00 -#define SAA7191_BANDPASS_MAX 0x03 -#define SAA7191_BANDPASS_DEFAULT 0x00 - -#define SAA7191_BANDPASS_WEIGHT_MIN 0x00 -#define SAA7191_BANDPASS_WEIGHT_MAX 0x03 -#define SAA7191_BANDPASS_WEIGHT_DEFAULT 0x01 - -#define SAA7191_CORING_MIN 0x00 -#define SAA7191_CORING_MAX 0x03 -#define SAA7191_CORING_DEFAULT 0x00 - -#define SAA7191_HUE_MIN 0x00 -#define SAA7191_HUE_MAX 0xff -#define SAA7191_HUE_DEFAULT 0x80 - -#define SAA7191_VTRC_MIN 0x00 -#define SAA7191_VTRC_MAX 0x01 -#define SAA7191_VTRC_DEFAULT 0x00 - -#define SAA7191_FORCE_COLOUR_MIN 0x00 -#define SAA7191_FORCE_COLOUR_MAX 0x01 -#define SAA7191_FORCE_COLOUR_DEFAULT 0x00 - -#define SAA7191_CHROMA_GAIN_MIN 0x00 -#define SAA7191_CHROMA_GAIN_MAX 0x03 -#define SAA7191_CHROMA_GAIN_DEFAULT 0x00 - -#define SAA7191_LUMA_DELAY_MIN -0x04 -#define SAA7191_LUMA_DELAY_MAX 0x03 -#define SAA7191_LUMA_DELAY_DEFAULT 0x01 - -#define SAA7191_VNR_MIN 0x00 -#define SAA7191_VNR_MAX 0x03 -#define SAA7191_VNR_DEFAULT 0x00 - -#define SAA7191_CONTROL_BANDPASS (V4L2_CID_PRIVATE_BASE + 0) -#define SAA7191_CONTROL_BANDPASS_WEIGHT (V4L2_CID_PRIVATE_BASE + 1) -#define SAA7191_CONTROL_CORING (V4L2_CID_PRIVATE_BASE + 2) -#define SAA7191_CONTROL_FORCE_COLOUR (V4L2_CID_PRIVATE_BASE + 3) -#define SAA7191_CONTROL_CHROMA_GAIN (V4L2_CID_PRIVATE_BASE + 4) -#define SAA7191_CONTROL_VTRC (V4L2_CID_PRIVATE_BASE + 5) -#define SAA7191_CONTROL_LUMA_DELAY (V4L2_CID_PRIVATE_BASE + 6) -#define SAA7191_CONTROL_VNR (V4L2_CID_PRIVATE_BASE + 7) - -#define DECODER_SAA7191_GET_STATUS _IOR('d', 195, struct saa7191_status) -#define DECODER_SAA7191_SET_NORM _IOW('d', 196, int) - -#endif diff --git a/drivers/media/video/smiapp-pll.c b/drivers/media/video/smiapp-pll.c deleted file mode 100644 index a2e41a21dc6..00000000000 --- a/drivers/media/video/smiapp-pll.c +++ /dev/null @@ -1,418 +0,0 @@ -/* - * drivers/media/video/smiapp-pll.c - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include -#include -#include - -#include "smiapp-pll.h" - -/* Return an even number or one. */ -static inline uint32_t clk_div_even(uint32_t a) -{ - return max_t(uint32_t, 1, a & ~1); -} - -/* Return an even number or one. */ -static inline uint32_t clk_div_even_up(uint32_t a) -{ - if (a == 1) - return 1; - return (a + 1) & ~1; -} - -static inline uint32_t is_one_or_even(uint32_t a) -{ - if (a == 1) - return 1; - if (a & 1) - return 0; - - return 1; -} - -static int bounds_check(struct device *dev, uint32_t val, - uint32_t min, uint32_t max, char *str) -{ - if (val >= min && val <= max) - return 0; - - dev_warn(dev, "%s out of bounds: %d (%d--%d)\n", str, val, min, max); - - return -EINVAL; -} - -static void print_pll(struct device *dev, struct smiapp_pll *pll) -{ - dev_dbg(dev, "pre_pll_clk_div\t%d\n", pll->pre_pll_clk_div); - dev_dbg(dev, "pll_multiplier \t%d\n", pll->pll_multiplier); - if (pll->flags != SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { - dev_dbg(dev, "op_sys_clk_div \t%d\n", pll->op_sys_clk_div); - dev_dbg(dev, "op_pix_clk_div \t%d\n", pll->op_pix_clk_div); - } - dev_dbg(dev, "vt_sys_clk_div \t%d\n", pll->vt_sys_clk_div); - dev_dbg(dev, "vt_pix_clk_div \t%d\n", pll->vt_pix_clk_div); - - dev_dbg(dev, "ext_clk_freq_hz \t%d\n", pll->ext_clk_freq_hz); - dev_dbg(dev, "pll_ip_clk_freq_hz \t%d\n", pll->pll_ip_clk_freq_hz); - dev_dbg(dev, "pll_op_clk_freq_hz \t%d\n", pll->pll_op_clk_freq_hz); - if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { - dev_dbg(dev, "op_sys_clk_freq_hz \t%d\n", - pll->op_sys_clk_freq_hz); - dev_dbg(dev, "op_pix_clk_freq_hz \t%d\n", - pll->op_pix_clk_freq_hz); - } - dev_dbg(dev, "vt_sys_clk_freq_hz \t%d\n", pll->vt_sys_clk_freq_hz); - dev_dbg(dev, "vt_pix_clk_freq_hz \t%d\n", pll->vt_pix_clk_freq_hz); -} - -int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits, - struct smiapp_pll *pll) -{ - uint32_t sys_div; - uint32_t best_pix_div = INT_MAX >> 1; - uint32_t vt_op_binning_div; - uint32_t lane_op_clock_ratio; - uint32_t mul, div; - uint32_t more_mul_min, more_mul_max; - uint32_t more_mul_factor; - uint32_t min_vt_div, max_vt_div, vt_div; - uint32_t min_sys_div, max_sys_div; - unsigned int i; - int rval; - - if (pll->flags & SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE) - lane_op_clock_ratio = pll->lanes; - else - lane_op_clock_ratio = 1; - dev_dbg(dev, "lane_op_clock_ratio: %d\n", lane_op_clock_ratio); - - dev_dbg(dev, "binning: %dx%d\n", pll->binning_horizontal, - pll->binning_vertical); - - /* CSI transfers 2 bits per clock per lane; thus times 2 */ - pll->pll_op_clk_freq_hz = pll->link_freq * 2 - * (pll->lanes / lane_op_clock_ratio); - - /* Figure out limits for pre-pll divider based on extclk */ - dev_dbg(dev, "min / max pre_pll_clk_div: %d / %d\n", - limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); - limits->max_pre_pll_clk_div = - min_t(uint16_t, limits->max_pre_pll_clk_div, - clk_div_even(pll->ext_clk_freq_hz / - limits->min_pll_ip_freq_hz)); - limits->min_pre_pll_clk_div = - max_t(uint16_t, limits->min_pre_pll_clk_div, - clk_div_even_up( - DIV_ROUND_UP(pll->ext_clk_freq_hz, - limits->max_pll_ip_freq_hz))); - dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %d / %d\n", - limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); - - i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz); - mul = div_u64(pll->pll_op_clk_freq_hz, i); - div = pll->ext_clk_freq_hz / i; - dev_dbg(dev, "mul %d / div %d\n", mul, div); - - limits->min_pre_pll_clk_div = - max_t(uint16_t, limits->min_pre_pll_clk_div, - clk_div_even_up( - DIV_ROUND_UP(mul * pll->ext_clk_freq_hz, - limits->max_pll_op_freq_hz))); - dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %d / %d\n", - limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); - - if (limits->min_pre_pll_clk_div > limits->max_pre_pll_clk_div) { - dev_err(dev, "unable to compute pre_pll divisor\n"); - return -EINVAL; - } - - pll->pre_pll_clk_div = limits->min_pre_pll_clk_div; - - /* - * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be - * too high. - */ - dev_dbg(dev, "pre_pll_clk_div %d\n", pll->pre_pll_clk_div); - - /* Don't go above max pll multiplier. */ - more_mul_max = limits->max_pll_multiplier / mul; - dev_dbg(dev, "more_mul_max: max_pll_multiplier check: %d\n", - more_mul_max); - /* Don't go above max pll op frequency. */ - more_mul_max = - min_t(int, - more_mul_max, - limits->max_pll_op_freq_hz - / (pll->ext_clk_freq_hz / pll->pre_pll_clk_div * mul)); - dev_dbg(dev, "more_mul_max: max_pll_op_freq_hz check: %d\n", - more_mul_max); - /* Don't go above the division capability of op sys clock divider. */ - more_mul_max = min(more_mul_max, - limits->max_op_sys_clk_div * pll->pre_pll_clk_div - / div); - dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %d\n", - more_mul_max); - /* Ensure we won't go above min_pll_multiplier. */ - more_mul_max = min(more_mul_max, - DIV_ROUND_UP(limits->max_pll_multiplier, mul)); - dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %d\n", - more_mul_max); - - /* Ensure we won't go below min_pll_op_freq_hz. */ - more_mul_min = DIV_ROUND_UP(limits->min_pll_op_freq_hz, - pll->ext_clk_freq_hz / pll->pre_pll_clk_div - * mul); - dev_dbg(dev, "more_mul_min: min_pll_op_freq_hz check: %d\n", - more_mul_min); - /* Ensure we won't go below min_pll_multiplier. */ - more_mul_min = max(more_mul_min, - DIV_ROUND_UP(limits->min_pll_multiplier, mul)); - dev_dbg(dev, "more_mul_min: min_pll_multiplier check: %d\n", - more_mul_min); - - if (more_mul_min > more_mul_max) { - dev_warn(dev, - "unable to compute more_mul_min and more_mul_max"); - return -EINVAL; - } - - more_mul_factor = lcm(div, pll->pre_pll_clk_div) / div; - dev_dbg(dev, "more_mul_factor: %d\n", more_mul_factor); - more_mul_factor = lcm(more_mul_factor, limits->min_op_sys_clk_div); - dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n", - more_mul_factor); - i = roundup(more_mul_min, more_mul_factor); - if (!is_one_or_even(i)) - i <<= 1; - - dev_dbg(dev, "final more_mul: %d\n", i); - if (i > more_mul_max) { - dev_warn(dev, "final more_mul is bad, max %d", more_mul_max); - return -EINVAL; - } - - pll->pll_multiplier = mul * i; - pll->op_sys_clk_div = div * i / pll->pre_pll_clk_div; - dev_dbg(dev, "op_sys_clk_div: %d\n", pll->op_sys_clk_div); - - pll->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz - / pll->pre_pll_clk_div; - - pll->pll_op_clk_freq_hz = pll->pll_ip_clk_freq_hz - * pll->pll_multiplier; - - /* Derive pll_op_clk_freq_hz. */ - pll->op_sys_clk_freq_hz = - pll->pll_op_clk_freq_hz / pll->op_sys_clk_div; - - pll->op_pix_clk_div = pll->bits_per_pixel; - dev_dbg(dev, "op_pix_clk_div: %d\n", pll->op_pix_clk_div); - - pll->op_pix_clk_freq_hz = - pll->op_sys_clk_freq_hz / pll->op_pix_clk_div; - - /* - * Some sensors perform analogue binning and some do this - * digitally. The ones doing this digitally can be roughly be - * found out using this formula. The ones doing this digitally - * should run at higher clock rate, so smaller divisor is used - * on video timing side. - */ - if (limits->min_line_length_pck_bin > limits->min_line_length_pck - / pll->binning_horizontal) - vt_op_binning_div = pll->binning_horizontal; - else - vt_op_binning_div = 1; - dev_dbg(dev, "vt_op_binning_div: %d\n", vt_op_binning_div); - - /* - * Profile 2 supports vt_pix_clk_div E [4, 10] - * - * Horizontal binning can be used as a base for difference in - * divisors. One must make sure that horizontal blanking is - * enough to accommodate the CSI-2 sync codes. - * - * Take scaling factor into account as well. - * - * Find absolute limits for the factor of vt divider. - */ - dev_dbg(dev, "scale_m: %d\n", pll->scale_m); - min_vt_div = DIV_ROUND_UP(pll->op_pix_clk_div * pll->op_sys_clk_div - * pll->scale_n, - lane_op_clock_ratio * vt_op_binning_div - * pll->scale_m); - - /* Find smallest and biggest allowed vt divisor. */ - dev_dbg(dev, "min_vt_div: %d\n", min_vt_div); - min_vt_div = max(min_vt_div, - DIV_ROUND_UP(pll->pll_op_clk_freq_hz, - limits->max_vt_pix_clk_freq_hz)); - dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %d\n", - min_vt_div); - min_vt_div = max_t(uint32_t, min_vt_div, - limits->min_vt_pix_clk_div - * limits->min_vt_sys_clk_div); - dev_dbg(dev, "min_vt_div: min_vt_clk_div: %d\n", min_vt_div); - - max_vt_div = limits->max_vt_sys_clk_div * limits->max_vt_pix_clk_div; - dev_dbg(dev, "max_vt_div: %d\n", max_vt_div); - max_vt_div = min(max_vt_div, - DIV_ROUND_UP(pll->pll_op_clk_freq_hz, - limits->min_vt_pix_clk_freq_hz)); - dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %d\n", - max_vt_div); - - /* - * Find limitsits for sys_clk_div. Not all values are possible - * with all values of pix_clk_div. - */ - min_sys_div = limits->min_vt_sys_clk_div; - dev_dbg(dev, "min_sys_div: %d\n", min_sys_div); - min_sys_div = max(min_sys_div, - DIV_ROUND_UP(min_vt_div, - limits->max_vt_pix_clk_div)); - dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %d\n", min_sys_div); - min_sys_div = max(min_sys_div, - pll->pll_op_clk_freq_hz - / limits->max_vt_sys_clk_freq_hz); - dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %d\n", min_sys_div); - min_sys_div = clk_div_even_up(min_sys_div); - dev_dbg(dev, "min_sys_div: one or even: %d\n", min_sys_div); - - max_sys_div = limits->max_vt_sys_clk_div; - dev_dbg(dev, "max_sys_div: %d\n", max_sys_div); - max_sys_div = min(max_sys_div, - DIV_ROUND_UP(max_vt_div, - limits->min_vt_pix_clk_div)); - dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %d\n", max_sys_div); - max_sys_div = min(max_sys_div, - DIV_ROUND_UP(pll->pll_op_clk_freq_hz, - limits->min_vt_pix_clk_freq_hz)); - dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %d\n", max_sys_div); - - /* - * Find pix_div such that a legal pix_div * sys_div results - * into a value which is not smaller than div, the desired - * divisor. - */ - for (vt_div = min_vt_div; vt_div <= max_vt_div; - vt_div += 2 - (vt_div & 1)) { - for (sys_div = min_sys_div; - sys_div <= max_sys_div; - sys_div += 2 - (sys_div & 1)) { - int pix_div = DIV_ROUND_UP(vt_div, sys_div); - - if (pix_div < limits->min_vt_pix_clk_div - || pix_div > limits->max_vt_pix_clk_div) { - dev_dbg(dev, - "pix_div %d too small or too big (%d--%d)\n", - pix_div, - limits->min_vt_pix_clk_div, - limits->max_vt_pix_clk_div); - continue; - } - - /* Check if this one is better. */ - if (pix_div * sys_div - <= roundup(min_vt_div, best_pix_div)) - best_pix_div = pix_div; - } - if (best_pix_div < INT_MAX >> 1) - break; - } - - pll->vt_sys_clk_div = DIV_ROUND_UP(min_vt_div, best_pix_div); - pll->vt_pix_clk_div = best_pix_div; - - pll->vt_sys_clk_freq_hz = - pll->pll_op_clk_freq_hz / pll->vt_sys_clk_div; - pll->vt_pix_clk_freq_hz = - pll->vt_sys_clk_freq_hz / pll->vt_pix_clk_div; - - pll->pixel_rate_csi = - pll->op_pix_clk_freq_hz * lane_op_clock_ratio; - - print_pll(dev, pll); - - rval = bounds_check(dev, pll->pre_pll_clk_div, - limits->min_pre_pll_clk_div, - limits->max_pre_pll_clk_div, "pre_pll_clk_div"); - if (!rval) - rval = bounds_check( - dev, pll->pll_ip_clk_freq_hz, - limits->min_pll_ip_freq_hz, limits->max_pll_ip_freq_hz, - "pll_ip_clk_freq_hz"); - if (!rval) - rval = bounds_check( - dev, pll->pll_multiplier, - limits->min_pll_multiplier, limits->max_pll_multiplier, - "pll_multiplier"); - if (!rval) - rval = bounds_check( - dev, pll->pll_op_clk_freq_hz, - limits->min_pll_op_freq_hz, limits->max_pll_op_freq_hz, - "pll_op_clk_freq_hz"); - if (!rval) - rval = bounds_check( - dev, pll->op_sys_clk_div, - limits->min_op_sys_clk_div, limits->max_op_sys_clk_div, - "op_sys_clk_div"); - if (!rval) - rval = bounds_check( - dev, pll->op_pix_clk_div, - limits->min_op_pix_clk_div, limits->max_op_pix_clk_div, - "op_pix_clk_div"); - if (!rval) - rval = bounds_check( - dev, pll->op_sys_clk_freq_hz, - limits->min_op_sys_clk_freq_hz, - limits->max_op_sys_clk_freq_hz, - "op_sys_clk_freq_hz"); - if (!rval) - rval = bounds_check( - dev, pll->op_pix_clk_freq_hz, - limits->min_op_pix_clk_freq_hz, - limits->max_op_pix_clk_freq_hz, - "op_pix_clk_freq_hz"); - if (!rval) - rval = bounds_check( - dev, pll->vt_sys_clk_freq_hz, - limits->min_vt_sys_clk_freq_hz, - limits->max_vt_sys_clk_freq_hz, - "vt_sys_clk_freq_hz"); - if (!rval) - rval = bounds_check( - dev, pll->vt_pix_clk_freq_hz, - limits->min_vt_pix_clk_freq_hz, - limits->max_vt_pix_clk_freq_hz, - "vt_pix_clk_freq_hz"); - - return rval; -} -EXPORT_SYMBOL_GPL(smiapp_pll_calculate); - -MODULE_AUTHOR("Sakari Ailus "); -MODULE_DESCRIPTION("Generic SMIA/SMIA++ PLL calculator"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/smiapp-pll.h b/drivers/media/video/smiapp-pll.h deleted file mode 100644 index 9eab63f23af..00000000000 --- a/drivers/media/video/smiapp-pll.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * drivers/media/video/smiapp-pll.h - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2012 Nokia Corporation - * Contact: Sakari Ailus - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#ifndef SMIAPP_PLL_H -#define SMIAPP_PLL_H - -#include - -struct smiapp_pll { - uint8_t lanes; - uint8_t binning_horizontal; - uint8_t binning_vertical; - uint8_t scale_m; - uint8_t scale_n; - uint8_t bits_per_pixel; - uint16_t flags; - uint32_t link_freq; - - uint16_t pre_pll_clk_div; - uint16_t pll_multiplier; - uint16_t op_sys_clk_div; - uint16_t op_pix_clk_div; - uint16_t vt_sys_clk_div; - uint16_t vt_pix_clk_div; - - uint32_t ext_clk_freq_hz; - uint32_t pll_ip_clk_freq_hz; - uint32_t pll_op_clk_freq_hz; - uint32_t op_sys_clk_freq_hz; - uint32_t op_pix_clk_freq_hz; - uint32_t vt_sys_clk_freq_hz; - uint32_t vt_pix_clk_freq_hz; - - uint32_t pixel_rate_csi; -}; - -struct smiapp_pll_limits { - /* Strict PLL limits */ - uint32_t min_ext_clk_freq_hz; - uint32_t max_ext_clk_freq_hz; - uint16_t min_pre_pll_clk_div; - uint16_t max_pre_pll_clk_div; - uint32_t min_pll_ip_freq_hz; - uint32_t max_pll_ip_freq_hz; - uint16_t min_pll_multiplier; - uint16_t max_pll_multiplier; - uint32_t min_pll_op_freq_hz; - uint32_t max_pll_op_freq_hz; - - uint16_t min_vt_sys_clk_div; - uint16_t max_vt_sys_clk_div; - uint32_t min_vt_sys_clk_freq_hz; - uint32_t max_vt_sys_clk_freq_hz; - uint16_t min_vt_pix_clk_div; - uint16_t max_vt_pix_clk_div; - uint32_t min_vt_pix_clk_freq_hz; - uint32_t max_vt_pix_clk_freq_hz; - - uint16_t min_op_sys_clk_div; - uint16_t max_op_sys_clk_div; - uint32_t min_op_sys_clk_freq_hz; - uint32_t max_op_sys_clk_freq_hz; - uint16_t min_op_pix_clk_div; - uint16_t max_op_pix_clk_div; - uint32_t min_op_pix_clk_freq_hz; - uint32_t max_op_pix_clk_freq_hz; - - /* Other relevant limits */ - uint32_t min_line_length_pck_bin; - uint32_t min_line_length_pck; -}; - -/* op pix clock is for all lanes in total normally */ -#define SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0) -#define SMIAPP_PLL_FLAG_NO_OP_CLOCKS (1 << 1) - -struct device; - -int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits, - struct smiapp_pll *pll); - -#endif /* SMIAPP_PLL_H */ diff --git a/drivers/media/video/smiapp/Kconfig b/drivers/media/video/smiapp/Kconfig deleted file mode 100644 index 3149cda1d0d..00000000000 --- a/drivers/media/video/smiapp/Kconfig +++ /dev/null @@ -1,7 +0,0 @@ -config VIDEO_SMIAPP - tristate "SMIA++/SMIA sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAVE_CLK - depends on MEDIA_CAMERA_SUPPORT - select VIDEO_SMIAPP_PLL - ---help--- - This is a generic driver for SMIA++/SMIA camera modules. diff --git a/drivers/media/video/smiapp/Makefile b/drivers/media/video/smiapp/Makefile deleted file mode 100644 index 36b0cfa2c54..00000000000 --- a/drivers/media/video/smiapp/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -smiapp-objs += smiapp-core.o smiapp-regs.o \ - smiapp-quirk.o smiapp-limits.o -obj-$(CONFIG_VIDEO_SMIAPP) += smiapp.o - -ccflags-y += -Idrivers/media/video diff --git a/drivers/media/video/smiapp/smiapp-core.c b/drivers/media/video/smiapp/smiapp-core.c deleted file mode 100644 index bfd47c10613..00000000000 --- a/drivers/media/video/smiapp/smiapp-core.c +++ /dev/null @@ -1,2895 +0,0 @@ -/* - * drivers/media/video/smiapp/smiapp-core.c - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2010--2012 Nokia Corporation - * Contact: Sakari Ailus - * - * Based on smiapp driver by Vimarsh Zutshi - * Based on jt8ev1.c by Vimarsh Zutshi - * Based on smia-sensor.c by Tuukka Toivonen - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "smiapp.h" - -#define SMIAPP_ALIGN_DIM(dim, flags) \ - ((flags) & V4L2_SEL_FLAG_GE \ - ? ALIGN((dim), 2) \ - : (dim) & ~1) - -/* - * smiapp_module_idents - supported camera modules - */ -static const struct smiapp_module_ident smiapp_module_idents[] = { - SMIAPP_IDENT_L(0x01, 0x022b, -1, "vs6555"), - SMIAPP_IDENT_L(0x01, 0x022e, -1, "vw6558"), - SMIAPP_IDENT_L(0x07, 0x7698, -1, "ovm7698"), - SMIAPP_IDENT_L(0x0b, 0x4242, -1, "smiapp-003"), - SMIAPP_IDENT_L(0x0c, 0x208a, -1, "tcm8330md"), - SMIAPP_IDENT_LQ(0x0c, 0x2134, -1, "tcm8500md", &smiapp_tcm8500md_quirk), - SMIAPP_IDENT_L(0x0c, 0x213e, -1, "et8en2"), - SMIAPP_IDENT_L(0x0c, 0x2184, -1, "tcm8580md"), - SMIAPP_IDENT_LQ(0x0c, 0x560f, -1, "jt8ew9", &smiapp_jt8ew9_quirk), - SMIAPP_IDENT_LQ(0x10, 0x4141, -1, "jt8ev1", &smiapp_jt8ev1_quirk), - SMIAPP_IDENT_LQ(0x10, 0x4241, -1, "imx125es", &smiapp_imx125es_quirk), -}; - -/* - * - * Dynamic Capability Identification - * - */ - -static int smiapp_read_frame_fmt(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - u32 fmt_model_type, fmt_model_subtype, ncol_desc, nrow_desc; - unsigned int i; - int rval; - int line_count = 0; - int embedded_start = -1, embedded_end = -1; - int image_start = 0; - - rval = smiapp_read(sensor, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE, - &fmt_model_type); - if (rval) - return rval; - - rval = smiapp_read(sensor, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE, - &fmt_model_subtype); - if (rval) - return rval; - - ncol_desc = (fmt_model_subtype - & SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_MASK) - >> SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_SHIFT; - nrow_desc = fmt_model_subtype - & SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NROWS_MASK; - - dev_dbg(&client->dev, "format_model_type %s\n", - fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE - ? "2 byte" : - fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE - ? "4 byte" : "is simply bad"); - - for (i = 0; i < ncol_desc + nrow_desc; i++) { - u32 desc; - u32 pixelcode; - u32 pixels; - char *which; - char *what; - - if (fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE) { - rval = smiapp_read( - sensor, - SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(i), - &desc); - if (rval) - return rval; - - pixelcode = - (desc - & SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_MASK) - >> SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_SHIFT; - pixels = desc & SMIAPP_FRAME_FORMAT_DESC_2_PIXELS_MASK; - } else if (fmt_model_type - == SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE) { - rval = smiapp_read( - sensor, - SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(i), - &desc); - if (rval) - return rval; - - pixelcode = - (desc - & SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_MASK) - >> SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_SHIFT; - pixels = desc & SMIAPP_FRAME_FORMAT_DESC_4_PIXELS_MASK; - } else { - dev_dbg(&client->dev, - "invalid frame format model type %d\n", - fmt_model_type); - return -EINVAL; - } - - if (i < ncol_desc) - which = "columns"; - else - which = "rows"; - - switch (pixelcode) { - case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED: - what = "embedded"; - break; - case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DUMMY: - what = "dummy"; - break; - case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_BLACK: - what = "black"; - break; - case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DARK: - what = "dark"; - break; - case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE: - what = "visible"; - break; - default: - what = "invalid"; - dev_dbg(&client->dev, "pixelcode %d\n", pixelcode); - break; - } - - dev_dbg(&client->dev, "%s pixels: %d %s\n", - what, pixels, which); - - if (i < ncol_desc) - continue; - - /* Handle row descriptors */ - if (pixelcode - == SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED) { - embedded_start = line_count; - } else { - if (pixelcode == SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE - || pixels >= sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES] / 2) - image_start = line_count; - if (embedded_start != -1 && embedded_end == -1) - embedded_end = line_count; - } - line_count += pixels; - } - - if (embedded_start == -1 || embedded_end == -1) { - embedded_start = 0; - embedded_end = 0; - } - - dev_dbg(&client->dev, "embedded data from lines %d to %d\n", - embedded_start, embedded_end); - dev_dbg(&client->dev, "image data starts at line %d\n", image_start); - - return 0; -} - -static int smiapp_pll_configure(struct smiapp_sensor *sensor) -{ - struct smiapp_pll *pll = &sensor->pll; - int rval; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_VT_PIX_CLK_DIV, pll->vt_pix_clk_div); - if (rval < 0) - return rval; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_VT_SYS_CLK_DIV, pll->vt_sys_clk_div); - if (rval < 0) - return rval; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_PRE_PLL_CLK_DIV, pll->pre_pll_clk_div); - if (rval < 0) - return rval; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_PLL_MULTIPLIER, pll->pll_multiplier); - if (rval < 0) - return rval; - - /* Lane op clock ratio does not apply here. */ - rval = smiapp_write( - sensor, SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS, - DIV_ROUND_UP(pll->op_sys_clk_freq_hz, 1000000 / 256 / 256)); - if (rval < 0 || sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) - return rval; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_OP_PIX_CLK_DIV, pll->op_pix_clk_div); - if (rval < 0) - return rval; - - return smiapp_write( - sensor, SMIAPP_REG_U16_OP_SYS_CLK_DIV, pll->op_sys_clk_div); -} - -static int smiapp_pll_update(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - struct smiapp_pll_limits lim = { - .min_pre_pll_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_PRE_PLL_CLK_DIV], - .max_pre_pll_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_PRE_PLL_CLK_DIV], - .min_pll_ip_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ], - .max_pll_ip_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_PLL_IP_FREQ_HZ], - .min_pll_multiplier = sensor->limits[SMIAPP_LIMIT_MIN_PLL_MULTIPLIER], - .max_pll_multiplier = sensor->limits[SMIAPP_LIMIT_MAX_PLL_MULTIPLIER], - .min_pll_op_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_PLL_OP_FREQ_HZ], - .max_pll_op_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_PLL_OP_FREQ_HZ], - - .min_op_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV], - .max_op_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV], - .min_op_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV], - .max_op_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV], - .min_op_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_FREQ_HZ], - .max_op_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_FREQ_HZ], - .min_op_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_FREQ_HZ], - .max_op_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_FREQ_HZ], - - .min_vt_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_DIV], - .max_vt_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_DIV], - .min_vt_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_DIV], - .max_vt_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_DIV], - .min_vt_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_FREQ_HZ], - .max_vt_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_FREQ_HZ], - .min_vt_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_FREQ_HZ], - .max_vt_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_FREQ_HZ], - - .min_line_length_pck_bin = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN], - .min_line_length_pck = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK], - }; - struct smiapp_pll *pll = &sensor->pll; - int rval; - - memset(&sensor->pll, 0, sizeof(sensor->pll)); - - pll->lanes = sensor->platform_data->lanes; - pll->ext_clk_freq_hz = sensor->platform_data->ext_clk; - - if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) { - /* - * Fill in operational clock divisors limits from the - * video timing ones. On profile 0 sensors the - * requirements regarding them are essentially the - * same as on VT ones. - */ - lim.min_op_sys_clk_div = lim.min_vt_sys_clk_div; - lim.max_op_sys_clk_div = lim.max_vt_sys_clk_div; - lim.min_op_pix_clk_div = lim.min_vt_pix_clk_div; - lim.max_op_pix_clk_div = lim.max_vt_pix_clk_div; - lim.min_op_sys_clk_freq_hz = lim.min_vt_sys_clk_freq_hz; - lim.max_op_sys_clk_freq_hz = lim.max_vt_sys_clk_freq_hz; - lim.min_op_pix_clk_freq_hz = lim.min_vt_pix_clk_freq_hz; - lim.max_op_pix_clk_freq_hz = lim.max_vt_pix_clk_freq_hz; - /* Profile 0 sensors have no separate OP clock branch. */ - pll->flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS; - } - - if (smiapp_needs_quirk(sensor, - SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE)) - pll->flags |= SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE; - - pll->binning_horizontal = sensor->binning_horizontal; - pll->binning_vertical = sensor->binning_vertical; - pll->link_freq = - sensor->link_freq->qmenu_int[sensor->link_freq->val]; - pll->scale_m = sensor->scale_m; - pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; - pll->bits_per_pixel = sensor->csi_format->compressed; - - rval = smiapp_pll_calculate(&client->dev, &lim, pll); - if (rval < 0) - return rval; - - sensor->pixel_rate_parray->cur.val64 = pll->vt_pix_clk_freq_hz; - sensor->pixel_rate_csi->cur.val64 = pll->pixel_rate_csi; - - return 0; -} - - -/* - * - * V4L2 Controls handling - * - */ - -static void __smiapp_update_exposure_limits(struct smiapp_sensor *sensor) -{ - struct v4l2_ctrl *ctrl = sensor->exposure; - int max; - - max = sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height - + sensor->vblank->val - - sensor->limits[SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MAX_MARGIN]; - - ctrl->maximum = max; - if (ctrl->default_value > max) - ctrl->default_value = max; - if (ctrl->val > max) - ctrl->val = max; - if (ctrl->cur.val > max) - ctrl->cur.val = max; -} - -/* - * Order matters. - * - * 1. Bits-per-pixel, descending. - * 2. Bits-per-pixel compressed, descending. - * 3. Pixel order, same as in pixel_order_str. Formats for all four pixel - * orders must be defined. - */ -static const struct smiapp_csi_data_format smiapp_csi_data_formats[] = { - { V4L2_MBUS_FMT_SGRBG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GRBG, }, - { V4L2_MBUS_FMT_SRGGB12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_RGGB, }, - { V4L2_MBUS_FMT_SBGGR12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_BGGR, }, - { V4L2_MBUS_FMT_SGBRG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GBRG, }, - { V4L2_MBUS_FMT_SGRBG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GRBG, }, - { V4L2_MBUS_FMT_SRGGB10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_RGGB, }, - { V4L2_MBUS_FMT_SBGGR10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_BGGR, }, - { V4L2_MBUS_FMT_SGBRG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GBRG, }, - { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GRBG, }, - { V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_RGGB, }, - { V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_BGGR, }, - { V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GBRG, }, - { V4L2_MBUS_FMT_SGRBG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GRBG, }, - { V4L2_MBUS_FMT_SRGGB8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_RGGB, }, - { V4L2_MBUS_FMT_SBGGR8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_BGGR, }, - { V4L2_MBUS_FMT_SGBRG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GBRG, }, -}; - -const char *pixel_order_str[] = { "GRBG", "RGGB", "BGGR", "GBRG" }; - -#define to_csi_format_idx(fmt) (((unsigned long)(fmt) \ - - (unsigned long)smiapp_csi_data_formats) \ - / sizeof(*smiapp_csi_data_formats)) - -static u32 smiapp_pixel_order(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - int flip = 0; - - if (sensor->hflip) { - if (sensor->hflip->val) - flip |= SMIAPP_IMAGE_ORIENTATION_HFLIP; - - if (sensor->vflip->val) - flip |= SMIAPP_IMAGE_ORIENTATION_VFLIP; - } - - flip ^= sensor->hvflip_inv_mask; - - dev_dbg(&client->dev, "flip %d\n", flip); - return sensor->default_pixel_order ^ flip; -} - -static void smiapp_update_mbus_formats(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - unsigned int csi_format_idx = - to_csi_format_idx(sensor->csi_format) & ~3; - unsigned int internal_csi_format_idx = - to_csi_format_idx(sensor->internal_csi_format) & ~3; - unsigned int pixel_order = smiapp_pixel_order(sensor); - - sensor->mbus_frame_fmts = - sensor->default_mbus_frame_fmts << pixel_order; - sensor->csi_format = - &smiapp_csi_data_formats[csi_format_idx + pixel_order]; - sensor->internal_csi_format = - &smiapp_csi_data_formats[internal_csi_format_idx - + pixel_order]; - - BUG_ON(max(internal_csi_format_idx, csi_format_idx) + pixel_order - >= ARRAY_SIZE(smiapp_csi_data_formats)); - BUG_ON(min(internal_csi_format_idx, csi_format_idx) < 0); - - dev_dbg(&client->dev, "new pixel order %s\n", - pixel_order_str[pixel_order]); -} - -static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl) -{ - struct smiapp_sensor *sensor = - container_of(ctrl->handler, struct smiapp_subdev, ctrl_handler) - ->sensor; - u32 orient = 0; - int exposure; - int rval; - - switch (ctrl->id) { - case V4L2_CID_ANALOGUE_GAIN: - return smiapp_write( - sensor, - SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL, ctrl->val); - - case V4L2_CID_EXPOSURE: - return smiapp_write( - sensor, - SMIAPP_REG_U16_COARSE_INTEGRATION_TIME, ctrl->val); - - case V4L2_CID_HFLIP: - case V4L2_CID_VFLIP: - if (sensor->streaming) - return -EBUSY; - - if (sensor->hflip->val) - orient |= SMIAPP_IMAGE_ORIENTATION_HFLIP; - - if (sensor->vflip->val) - orient |= SMIAPP_IMAGE_ORIENTATION_VFLIP; - - orient ^= sensor->hvflip_inv_mask; - rval = smiapp_write(sensor, - SMIAPP_REG_U8_IMAGE_ORIENTATION, - orient); - if (rval < 0) - return rval; - - smiapp_update_mbus_formats(sensor); - - return 0; - - case V4L2_CID_VBLANK: - exposure = sensor->exposure->val; - - __smiapp_update_exposure_limits(sensor); - - if (exposure > sensor->exposure->maximum) { - sensor->exposure->val = - sensor->exposure->maximum; - rval = smiapp_set_ctrl( - sensor->exposure); - if (rval < 0) - return rval; - } - - return smiapp_write( - sensor, SMIAPP_REG_U16_FRAME_LENGTH_LINES, - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height - + ctrl->val); - - case V4L2_CID_HBLANK: - return smiapp_write( - sensor, SMIAPP_REG_U16_LINE_LENGTH_PCK, - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width - + ctrl->val); - - case V4L2_CID_LINK_FREQ: - if (sensor->streaming) - return -EBUSY; - - return smiapp_pll_update(sensor); - - default: - return -EINVAL; - } -} - -static const struct v4l2_ctrl_ops smiapp_ctrl_ops = { - .s_ctrl = smiapp_set_ctrl, -}; - -static int smiapp_init_controls(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - unsigned int max; - int rval; - - rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 7); - if (rval) - return rval; - sensor->pixel_array->ctrl_handler.lock = &sensor->mutex; - - sensor->analog_gain = v4l2_ctrl_new_std( - &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_ANALOGUE_GAIN, - sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN], - sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX], - max(sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_STEP], 1U), - sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN]); - - /* Exposure limits will be updated soon, use just something here. */ - sensor->exposure = v4l2_ctrl_new_std( - &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_EXPOSURE, 0, 0, 1, 0); - - sensor->hflip = v4l2_ctrl_new_std( - &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - sensor->vflip = v4l2_ctrl_new_std( - &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - - sensor->vblank = v4l2_ctrl_new_std( - &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_VBLANK, 0, 1, 1, 0); - - if (sensor->vblank) - sensor->vblank->flags |= V4L2_CTRL_FLAG_UPDATE; - - sensor->hblank = v4l2_ctrl_new_std( - &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_HBLANK, 0, 1, 1, 0); - - if (sensor->hblank) - sensor->hblank->flags |= V4L2_CTRL_FLAG_UPDATE; - - sensor->pixel_rate_parray = v4l2_ctrl_new_std( - &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_PIXEL_RATE, 0, 0, 1, 0); - - if (sensor->pixel_array->ctrl_handler.error) { - dev_err(&client->dev, - "pixel array controls initialization failed (%d)\n", - sensor->pixel_array->ctrl_handler.error); - rval = sensor->pixel_array->ctrl_handler.error; - goto error; - } - - sensor->pixel_array->sd.ctrl_handler = - &sensor->pixel_array->ctrl_handler; - - v4l2_ctrl_cluster(2, &sensor->hflip); - - rval = v4l2_ctrl_handler_init(&sensor->src->ctrl_handler, 0); - if (rval) - goto error; - sensor->src->ctrl_handler.lock = &sensor->mutex; - - for (max = 0; sensor->platform_data->op_sys_clock[max + 1]; max++); - - sensor->link_freq = v4l2_ctrl_new_int_menu( - &sensor->src->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_LINK_FREQ, max, 0, - sensor->platform_data->op_sys_clock); - - sensor->pixel_rate_csi = v4l2_ctrl_new_std( - &sensor->src->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_PIXEL_RATE, 0, 0, 1, 0); - - if (sensor->src->ctrl_handler.error) { - dev_err(&client->dev, - "src controls initialization failed (%d)\n", - sensor->src->ctrl_handler.error); - rval = sensor->src->ctrl_handler.error; - goto error; - } - - sensor->src->sd.ctrl_handler = - &sensor->src->ctrl_handler; - - return 0; - -error: - v4l2_ctrl_handler_free(&sensor->pixel_array->ctrl_handler); - v4l2_ctrl_handler_free(&sensor->src->ctrl_handler); - - return rval; -} - -static void smiapp_free_controls(struct smiapp_sensor *sensor) -{ - unsigned int i; - - for (i = 0; i < sensor->ssds_used; i++) - v4l2_ctrl_handler_free(&sensor->ssds[i].ctrl_handler); -} - -static int smiapp_get_limits(struct smiapp_sensor *sensor, int const *limit, - unsigned int n) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - unsigned int i; - u32 val; - int rval; - - for (i = 0; i < n; i++) { - rval = smiapp_read( - sensor, smiapp_reg_limits[limit[i]].addr, &val); - if (rval) - return rval; - sensor->limits[limit[i]] = val; - dev_dbg(&client->dev, "0x%8.8x \"%s\" = %d, 0x%x\n", - smiapp_reg_limits[limit[i]].addr, - smiapp_reg_limits[limit[i]].what, val, val); - } - - return 0; -} - -static int smiapp_get_all_limits(struct smiapp_sensor *sensor) -{ - unsigned int i; - int rval; - - for (i = 0; i < SMIAPP_LIMIT_LAST; i++) { - rval = smiapp_get_limits(sensor, &i, 1); - if (rval < 0) - return rval; - } - - if (sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] == 0) - smiapp_replace_limit(sensor, SMIAPP_LIMIT_SCALER_N_MIN, 16); - - return 0; -} - -static int smiapp_get_limits_binning(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - static u32 const limits[] = { - SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN, - SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN, - SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN, - SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN, - SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN, - SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN_BIN, - SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN, - }; - static u32 const limits_replace[] = { - SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES, - SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES, - SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK, - SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK, - SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK, - SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN, - SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN, - }; - unsigned int i; - int rval; - - if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY] == - SMIAPP_BINNING_CAPABILITY_NO) { - for (i = 0; i < ARRAY_SIZE(limits); i++) - sensor->limits[limits[i]] = - sensor->limits[limits_replace[i]]; - - return 0; - } - - rval = smiapp_get_limits(sensor, limits, ARRAY_SIZE(limits)); - if (rval < 0) - return rval; - - /* - * Sanity check whether the binning limits are valid. If not, - * use the non-binning ones. - */ - if (sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN] - && sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN] - && sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]) - return 0; - - for (i = 0; i < ARRAY_SIZE(limits); i++) { - dev_dbg(&client->dev, - "replace limit 0x%8.8x \"%s\" = %d, 0x%x\n", - smiapp_reg_limits[limits[i]].addr, - smiapp_reg_limits[limits[i]].what, - sensor->limits[limits_replace[i]], - sensor->limits[limits_replace[i]]); - sensor->limits[limits[i]] = - sensor->limits[limits_replace[i]]; - } - - return 0; -} - -static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - unsigned int type, n; - unsigned int i, pixel_order; - int rval; - - rval = smiapp_read( - sensor, SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE, &type); - if (rval) - return rval; - - dev_dbg(&client->dev, "data_format_model_type %d\n", type); - - rval = smiapp_read(sensor, SMIAPP_REG_U8_PIXEL_ORDER, - &pixel_order); - if (rval) - return rval; - - if (pixel_order >= ARRAY_SIZE(pixel_order_str)) { - dev_dbg(&client->dev, "bad pixel order %d\n", pixel_order); - return -EINVAL; - } - - dev_dbg(&client->dev, "pixel order %d (%s)\n", pixel_order, - pixel_order_str[pixel_order]); - - switch (type) { - case SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL: - n = SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL_N; - break; - case SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED: - n = SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED_N; - break; - default: - return -EINVAL; - } - - sensor->default_pixel_order = pixel_order; - sensor->mbus_frame_fmts = 0; - - for (i = 0; i < n; i++) { - unsigned int fmt, j; - - rval = smiapp_read( - sensor, - SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(i), &fmt); - if (rval) - return rval; - - dev_dbg(&client->dev, "bpp %d, compressed %d\n", - fmt >> 8, (u8)fmt); - - for (j = 0; j < ARRAY_SIZE(smiapp_csi_data_formats); j++) { - const struct smiapp_csi_data_format *f = - &smiapp_csi_data_formats[j]; - - if (f->pixel_order != SMIAPP_PIXEL_ORDER_GRBG) - continue; - - if (f->width != fmt >> 8 || f->compressed != (u8)fmt) - continue; - - dev_dbg(&client->dev, "jolly good! %d\n", j); - - sensor->default_mbus_frame_fmts |= 1 << j; - if (!sensor->csi_format) { - sensor->csi_format = f; - sensor->internal_csi_format = f; - } - } - } - - if (!sensor->csi_format) { - dev_err(&client->dev, "no supported mbus code found\n"); - return -EINVAL; - } - - smiapp_update_mbus_formats(sensor); - - return 0; -} - -static void smiapp_update_blanking(struct smiapp_sensor *sensor) -{ - struct v4l2_ctrl *vblank = sensor->vblank; - struct v4l2_ctrl *hblank = sensor->hblank; - - vblank->minimum = - max_t(int, - sensor->limits[SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES], - sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN] - - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height); - vblank->maximum = - sensor->limits[SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN] - - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height; - - vblank->val = clamp_t(int, vblank->val, - vblank->minimum, vblank->maximum); - vblank->default_value = vblank->minimum; - vblank->val = vblank->val; - vblank->cur.val = vblank->val; - - hblank->minimum = - max_t(int, - sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN] - - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width, - sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]); - hblank->maximum = - sensor->limits[SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN] - - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width; - - hblank->val = clamp_t(int, hblank->val, - hblank->minimum, hblank->maximum); - hblank->default_value = hblank->minimum; - hblank->val = hblank->val; - hblank->cur.val = hblank->val; - - __smiapp_update_exposure_limits(sensor); -} - -static int smiapp_update_mode(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - unsigned int binning_mode; - int rval; - - dev_dbg(&client->dev, "frame size: %dx%d\n", - sensor->src->crop[SMIAPP_PAD_SRC].width, - sensor->src->crop[SMIAPP_PAD_SRC].height); - dev_dbg(&client->dev, "csi format width: %d\n", - sensor->csi_format->width); - - /* Binning has to be set up here; it affects limits */ - if (sensor->binning_horizontal == 1 && - sensor->binning_vertical == 1) { - binning_mode = 0; - } else { - u8 binning_type = - (sensor->binning_horizontal << 4) - | sensor->binning_vertical; - - rval = smiapp_write( - sensor, SMIAPP_REG_U8_BINNING_TYPE, binning_type); - if (rval < 0) - return rval; - - binning_mode = 1; - } - rval = smiapp_write(sensor, SMIAPP_REG_U8_BINNING_MODE, binning_mode); - if (rval < 0) - return rval; - - /* Get updated limits due to binning */ - rval = smiapp_get_limits_binning(sensor); - if (rval < 0) - return rval; - - rval = smiapp_pll_update(sensor); - if (rval < 0) - return rval; - - /* Output from pixel array, including blanking */ - smiapp_update_blanking(sensor); - - dev_dbg(&client->dev, "vblank\t\t%d\n", sensor->vblank->val); - dev_dbg(&client->dev, "hblank\t\t%d\n", sensor->hblank->val); - - dev_dbg(&client->dev, "real timeperframe\t100/%d\n", - sensor->pll.vt_pix_clk_freq_hz / - ((sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width - + sensor->hblank->val) * - (sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height - + sensor->vblank->val) / 100)); - - return 0; -} - -/* - * - * SMIA++ NVM handling - * - */ -static int smiapp_read_nvm(struct smiapp_sensor *sensor, - unsigned char *nvm) -{ - u32 i, s, p, np, v; - int rval = 0, rval2; - - np = sensor->nvm_size / SMIAPP_NVM_PAGE_SIZE; - for (p = 0; p < np; p++) { - rval = smiapp_write( - sensor, - SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT, p); - if (rval) - goto out; - - rval = smiapp_write(sensor, - SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, - SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN | - SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN); - if (rval) - goto out; - - for (i = 0; i < 1000; i++) { - rval = smiapp_read( - sensor, - SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS, &s); - - if (rval) - goto out; - - if (s & SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY) - break; - - if (--i == 0) { - rval = -ETIMEDOUT; - goto out; - } - - } - - for (i = 0; i < SMIAPP_NVM_PAGE_SIZE; i++) { - rval = smiapp_read( - sensor, - SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 + i, - &v); - if (rval) - goto out; - - *nvm++ = v; - } - } - -out: - rval2 = smiapp_write(sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, 0); - if (rval < 0) - return rval; - else - return rval2; -} - -/* - * - * SMIA++ CCI address control - * - */ -static int smiapp_change_cci_addr(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - int rval; - u32 val; - - client->addr = sensor->platform_data->i2c_addr_dfl; - - rval = smiapp_write(sensor, - SMIAPP_REG_U8_CCI_ADDRESS_CONTROL, - sensor->platform_data->i2c_addr_alt << 1); - if (rval) - return rval; - - client->addr = sensor->platform_data->i2c_addr_alt; - - /* verify addr change went ok */ - rval = smiapp_read(sensor, SMIAPP_REG_U8_CCI_ADDRESS_CONTROL, &val); - if (rval) - return rval; - - if (val != sensor->platform_data->i2c_addr_alt << 1) - return -ENODEV; - - return 0; -} - -/* - * - * SMIA++ Mode Control - * - */ -static int smiapp_setup_flash_strobe(struct smiapp_sensor *sensor) -{ - struct smiapp_flash_strobe_parms *strobe_setup; - unsigned int ext_freq = sensor->platform_data->ext_clk; - u32 tmp; - u32 strobe_adjustment; - u32 strobe_width_high_rs; - int rval; - - strobe_setup = sensor->platform_data->strobe_setup; - - /* - * How to calculate registers related to strobe length. Please - * do not change, or if you do at least know what you're - * doing. :-) - * - * Sakari Ailus 2010-10-25 - * - * flash_strobe_length [us] / 10^6 = (tFlash_strobe_width_ctrl - * / EXTCLK freq [Hz]) * flash_strobe_adjustment - * - * tFlash_strobe_width_ctrl E N, [1 - 0xffff] - * flash_strobe_adjustment E N, [1 - 0xff] - * - * The formula above is written as below to keep it on one - * line: - * - * l / 10^6 = w / e * a - * - * Let's mark w * a by x: - * - * x = w * a - * - * Thus, we get: - * - * x = l * e / 10^6 - * - * The strobe width must be at least as long as requested, - * thus rounding upwards is needed. - * - * x = (l * e + 10^6 - 1) / 10^6 - * ----------------------------- - * - * Maximum possible accuracy is wanted at all times. Thus keep - * a as small as possible. - * - * Calculate a, assuming maximum w, with rounding upwards: - * - * a = (x + (2^16 - 1) - 1) / (2^16 - 1) - * ------------------------------------- - * - * Thus, we also get w, with that a, with rounding upwards: - * - * w = (x + a - 1) / a - * ------------------- - * - * To get limits: - * - * x E [1, (2^16 - 1) * (2^8 - 1)] - * - * Substituting maximum x to the original formula (with rounding), - * the maximum l is thus - * - * (2^16 - 1) * (2^8 - 1) * 10^6 = l * e + 10^6 - 1 - * - * l = (10^6 * (2^16 - 1) * (2^8 - 1) - 10^6 + 1) / e - * -------------------------------------------------- - * - * flash_strobe_length must be clamped between 1 and - * (10^6 * (2^16 - 1) * (2^8 - 1) - 10^6 + 1) / EXTCLK freq. - * - * Then, - * - * flash_strobe_adjustment = ((flash_strobe_length * - * EXTCLK freq + 10^6 - 1) / 10^6 + (2^16 - 1) - 1) / (2^16 - 1) - * - * tFlash_strobe_width_ctrl = ((flash_strobe_length * - * EXTCLK freq + 10^6 - 1) / 10^6 + - * flash_strobe_adjustment - 1) / flash_strobe_adjustment - */ - tmp = div_u64(1000000ULL * ((1 << 16) - 1) * ((1 << 8) - 1) - - 1000000 + 1, ext_freq); - strobe_setup->strobe_width_high_us = - clamp_t(u32, strobe_setup->strobe_width_high_us, 1, tmp); - - tmp = div_u64(((u64)strobe_setup->strobe_width_high_us * (u64)ext_freq + - 1000000 - 1), 1000000ULL); - strobe_adjustment = (tmp + (1 << 16) - 1 - 1) / ((1 << 16) - 1); - strobe_width_high_rs = (tmp + strobe_adjustment - 1) / - strobe_adjustment; - - rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_MODE_RS, - strobe_setup->mode); - if (rval < 0) - goto out; - - rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT, - strobe_adjustment); - if (rval < 0) - goto out; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL, - strobe_width_high_rs); - if (rval < 0) - goto out; - - rval = smiapp_write(sensor, SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL, - strobe_setup->strobe_delay); - if (rval < 0) - goto out; - - rval = smiapp_write(sensor, SMIAPP_REG_U16_FLASH_STROBE_START_POINT, - strobe_setup->stobe_start_point); - if (rval < 0) - goto out; - - rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_TRIGGER_RS, - strobe_setup->trigger); - -out: - sensor->platform_data->strobe_setup->trigger = 0; - - return rval; -} - -/* ----------------------------------------------------------------------------- - * Power management - */ - -static int smiapp_power_on(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - unsigned int sleep; - int rval; - - rval = regulator_enable(sensor->vana); - if (rval) { - dev_err(&client->dev, "failed to enable vana regulator\n"); - return rval; - } - usleep_range(1000, 1000); - - if (sensor->platform_data->set_xclk) - rval = sensor->platform_data->set_xclk( - &sensor->src->sd, sensor->platform_data->ext_clk); - else - rval = clk_enable(sensor->ext_clk); - if (rval < 0) { - dev_dbg(&client->dev, "failed to set xclk\n"); - goto out_xclk_fail; - } - usleep_range(1000, 1000); - - if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) - gpio_set_value(sensor->platform_data->xshutdown, 1); - - sleep = SMIAPP_RESET_DELAY(sensor->platform_data->ext_clk); - usleep_range(sleep, sleep); - - /* - * Failures to respond to the address change command have been noticed. - * Those failures seem to be caused by the sensor requiring a longer - * boot time than advertised. An additional 10ms delay seems to work - * around the issue, but the SMIA++ I2C write retry hack makes the delay - * unnecessary. The failures need to be investigated to find a proper - * fix, and a delay will likely need to be added here if the I2C write - * retry hack is reverted before the root cause of the boot time issue - * is found. - */ - - if (sensor->platform_data->i2c_addr_alt) { - rval = smiapp_change_cci_addr(sensor); - if (rval) { - dev_err(&client->dev, "cci address change error\n"); - goto out_cci_addr_fail; - } - } - - rval = smiapp_write(sensor, SMIAPP_REG_U8_SOFTWARE_RESET, - SMIAPP_SOFTWARE_RESET); - if (rval < 0) { - dev_err(&client->dev, "software reset failed\n"); - goto out_cci_addr_fail; - } - - if (sensor->platform_data->i2c_addr_alt) { - rval = smiapp_change_cci_addr(sensor); - if (rval) { - dev_err(&client->dev, "cci address change error\n"); - goto out_cci_addr_fail; - } - } - - rval = smiapp_write(sensor, SMIAPP_REG_U16_COMPRESSION_MODE, - SMIAPP_COMPRESSION_MODE_SIMPLE_PREDICTOR); - if (rval) { - dev_err(&client->dev, "compression mode set failed\n"); - goto out_cci_addr_fail; - } - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ, - sensor->platform_data->ext_clk / (1000000 / (1 << 8))); - if (rval) { - dev_err(&client->dev, "extclk frequency set failed\n"); - goto out_cci_addr_fail; - } - - rval = smiapp_write(sensor, SMIAPP_REG_U8_CSI_LANE_MODE, - sensor->platform_data->lanes - 1); - if (rval) { - dev_err(&client->dev, "csi lane mode set failed\n"); - goto out_cci_addr_fail; - } - - rval = smiapp_write(sensor, SMIAPP_REG_U8_FAST_STANDBY_CTRL, - SMIAPP_FAST_STANDBY_CTRL_IMMEDIATE); - if (rval) { - dev_err(&client->dev, "fast standby set failed\n"); - goto out_cci_addr_fail; - } - - rval = smiapp_write(sensor, SMIAPP_REG_U8_CSI_SIGNALLING_MODE, - sensor->platform_data->csi_signalling_mode); - if (rval) { - dev_err(&client->dev, "csi signalling mode set failed\n"); - goto out_cci_addr_fail; - } - - /* DPHY control done by sensor based on requested link rate */ - rval = smiapp_write(sensor, SMIAPP_REG_U8_DPHY_CTRL, - SMIAPP_DPHY_CTRL_UI); - if (rval < 0) - return rval; - - rval = smiapp_call_quirk(sensor, post_poweron); - if (rval) { - dev_err(&client->dev, "post_poweron quirks failed\n"); - goto out_cci_addr_fail; - } - - /* Are we still initialising...? If yes, return here. */ - if (!sensor->pixel_array) - return 0; - - rval = v4l2_ctrl_handler_setup( - &sensor->pixel_array->ctrl_handler); - if (rval) - goto out_cci_addr_fail; - - rval = v4l2_ctrl_handler_setup(&sensor->src->ctrl_handler); - if (rval) - goto out_cci_addr_fail; - - mutex_lock(&sensor->mutex); - rval = smiapp_update_mode(sensor); - mutex_unlock(&sensor->mutex); - if (rval < 0) - goto out_cci_addr_fail; - - return 0; - -out_cci_addr_fail: - if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) - gpio_set_value(sensor->platform_data->xshutdown, 0); - if (sensor->platform_data->set_xclk) - sensor->platform_data->set_xclk(&sensor->src->sd, 0); - else - clk_disable(sensor->ext_clk); - -out_xclk_fail: - regulator_disable(sensor->vana); - return rval; -} - -static void smiapp_power_off(struct smiapp_sensor *sensor) -{ - /* - * Currently power/clock to lens are enable/disabled separately - * but they are essentially the same signals. So if the sensor is - * powered off while the lens is powered on the sensor does not - * really see a power off and next time the cci address change - * will fail. So do a soft reset explicitly here. - */ - if (sensor->platform_data->i2c_addr_alt) - smiapp_write(sensor, - SMIAPP_REG_U8_SOFTWARE_RESET, - SMIAPP_SOFTWARE_RESET); - - if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) - gpio_set_value(sensor->platform_data->xshutdown, 0); - if (sensor->platform_data->set_xclk) - sensor->platform_data->set_xclk(&sensor->src->sd, 0); - else - clk_disable(sensor->ext_clk); - usleep_range(5000, 5000); - regulator_disable(sensor->vana); - sensor->streaming = 0; -} - -static int smiapp_set_power(struct v4l2_subdev *subdev, int on) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - int ret = 0; - - mutex_lock(&sensor->power_mutex); - - /* - * If the power count is modified from 0 to != 0 or from != 0 - * to 0, update the power state. - */ - if (!sensor->power_count == !on) - goto out; - - if (on) { - /* Power on and perform initialisation. */ - ret = smiapp_power_on(sensor); - if (ret < 0) - goto out; - } else { - smiapp_power_off(sensor); - } - - /* Update the power count. */ - sensor->power_count += on ? 1 : -1; - WARN_ON(sensor->power_count < 0); - -out: - mutex_unlock(&sensor->power_mutex); - return ret; -} - -/* ----------------------------------------------------------------------------- - * Video stream management - */ - -static int smiapp_start_streaming(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - int rval; - - mutex_lock(&sensor->mutex); - - rval = smiapp_write(sensor, SMIAPP_REG_U16_CSI_DATA_FORMAT, - (sensor->csi_format->width << 8) | - sensor->csi_format->compressed); - if (rval) - goto out; - - rval = smiapp_pll_configure(sensor); - if (rval) - goto out; - - /* Analog crop start coordinates */ - rval = smiapp_write(sensor, SMIAPP_REG_U16_X_ADDR_START, - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].left); - if (rval < 0) - goto out; - - rval = smiapp_write(sensor, SMIAPP_REG_U16_Y_ADDR_START, - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].top); - if (rval < 0) - goto out; - - /* Analog crop end coordinates */ - rval = smiapp_write( - sensor, SMIAPP_REG_U16_X_ADDR_END, - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].left - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width - 1); - if (rval < 0) - goto out; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_Y_ADDR_END, - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].top - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height - 1); - if (rval < 0) - goto out; - - /* - * Output from pixel array, including blanking, is set using - * controls below. No need to set here. - */ - - /* Digital crop */ - if (sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] - == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) { - rval = smiapp_write( - sensor, SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET, - sensor->scaler->crop[SMIAPP_PAD_SINK].left); - if (rval < 0) - goto out; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET, - sensor->scaler->crop[SMIAPP_PAD_SINK].top); - if (rval < 0) - goto out; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH, - sensor->scaler->crop[SMIAPP_PAD_SINK].width); - if (rval < 0) - goto out; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT, - sensor->scaler->crop[SMIAPP_PAD_SINK].height); - if (rval < 0) - goto out; - } - - /* Scaling */ - if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] - != SMIAPP_SCALING_CAPABILITY_NONE) { - rval = smiapp_write(sensor, SMIAPP_REG_U16_SCALING_MODE, - sensor->scaling_mode); - if (rval < 0) - goto out; - - rval = smiapp_write(sensor, SMIAPP_REG_U16_SCALE_M, - sensor->scale_m); - if (rval < 0) - goto out; - } - - /* Output size from sensor */ - rval = smiapp_write(sensor, SMIAPP_REG_U16_X_OUTPUT_SIZE, - sensor->src->crop[SMIAPP_PAD_SRC].width); - if (rval < 0) - goto out; - rval = smiapp_write(sensor, SMIAPP_REG_U16_Y_OUTPUT_SIZE, - sensor->src->crop[SMIAPP_PAD_SRC].height); - if (rval < 0) - goto out; - - if ((sensor->flash_capability & - (SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE | - SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE)) && - sensor->platform_data->strobe_setup != NULL && - sensor->platform_data->strobe_setup->trigger != 0) { - rval = smiapp_setup_flash_strobe(sensor); - if (rval) - goto out; - } - - rval = smiapp_call_quirk(sensor, pre_streamon); - if (rval) { - dev_err(&client->dev, "pre_streamon quirks failed\n"); - goto out; - } - - rval = smiapp_write(sensor, SMIAPP_REG_U8_MODE_SELECT, - SMIAPP_MODE_SELECT_STREAMING); - -out: - mutex_unlock(&sensor->mutex); - - return rval; -} - -static int smiapp_stop_streaming(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - int rval; - - mutex_lock(&sensor->mutex); - rval = smiapp_write(sensor, SMIAPP_REG_U8_MODE_SELECT, - SMIAPP_MODE_SELECT_SOFTWARE_STANDBY); - if (rval) - goto out; - - rval = smiapp_call_quirk(sensor, post_streamoff); - if (rval) - dev_err(&client->dev, "post_streamoff quirks failed\n"); - -out: - mutex_unlock(&sensor->mutex); - return rval; -} - -/* ----------------------------------------------------------------------------- - * V4L2 subdev video operations - */ - -static int smiapp_set_stream(struct v4l2_subdev *subdev, int enable) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - int rval; - - if (sensor->streaming == enable) - return 0; - - if (enable) { - sensor->streaming = 1; - rval = smiapp_start_streaming(sensor); - if (rval < 0) - sensor->streaming = 0; - } else { - rval = smiapp_stop_streaming(sensor); - sensor->streaming = 0; - } - - return rval; -} - -static int smiapp_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_mbus_code_enum *code) -{ - struct i2c_client *client = v4l2_get_subdevdata(subdev); - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - unsigned int i; - int idx = -1; - int rval = -EINVAL; - - mutex_lock(&sensor->mutex); - - dev_err(&client->dev, "subdev %s, pad %d, index %d\n", - subdev->name, code->pad, code->index); - - if (subdev != &sensor->src->sd || code->pad != SMIAPP_PAD_SRC) { - if (code->index) - goto out; - - code->code = sensor->internal_csi_format->code; - rval = 0; - goto out; - } - - for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) { - if (sensor->mbus_frame_fmts & (1 << i)) - idx++; - - if (idx == code->index) { - code->code = smiapp_csi_data_formats[i].code; - dev_err(&client->dev, "found index %d, i %d, code %x\n", - code->index, i, code->code); - rval = 0; - break; - } - } - -out: - mutex_unlock(&sensor->mutex); - - return rval; -} - -static u32 __smiapp_get_mbus_code(struct v4l2_subdev *subdev, - unsigned int pad) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - - if (subdev == &sensor->src->sd && pad == SMIAPP_PAD_SRC) - return sensor->csi_format->code; - else - return sensor->internal_csi_format->code; -} - -static int __smiapp_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *fmt) -{ - struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt->format = *v4l2_subdev_get_try_format(fh, fmt->pad); - } else { - struct v4l2_rect *r; - - if (fmt->pad == ssd->source_pad) - r = &ssd->crop[ssd->source_pad]; - else - r = &ssd->sink_fmt; - - fmt->format.code = __smiapp_get_mbus_code(subdev, fmt->pad); - fmt->format.width = r->width; - fmt->format.height = r->height; - } - - return 0; -} - -static int smiapp_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *fmt) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - int rval; - - mutex_lock(&sensor->mutex); - rval = __smiapp_get_format(subdev, fh, fmt); - mutex_unlock(&sensor->mutex); - - return rval; -} - -static void smiapp_get_crop_compose(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_rect **crops, - struct v4l2_rect **comps, int which) -{ - struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); - unsigned int i; - - if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { - if (crops) - for (i = 0; i < subdev->entity.num_pads; i++) - crops[i] = &ssd->crop[i]; - if (comps) - *comps = &ssd->compose; - } else { - if (crops) { - for (i = 0; i < subdev->entity.num_pads; i++) { - crops[i] = v4l2_subdev_get_try_crop(fh, i); - BUG_ON(!crops[i]); - } - } - if (comps) { - *comps = v4l2_subdev_get_try_compose(fh, - SMIAPP_PAD_SINK); - BUG_ON(!*comps); - } - } -} - -/* Changes require propagation only on sink pad. */ -static void smiapp_propagate(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, int which, - int target) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); - struct v4l2_rect *comp, *crops[SMIAPP_PADS]; - - smiapp_get_crop_compose(subdev, fh, crops, &comp, which); - - switch (target) { - case V4L2_SEL_TGT_CROP: - comp->width = crops[SMIAPP_PAD_SINK]->width; - comp->height = crops[SMIAPP_PAD_SINK]->height; - if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { - if (ssd == sensor->scaler) { - sensor->scale_m = - sensor->limits[ - SMIAPP_LIMIT_SCALER_N_MIN]; - sensor->scaling_mode = - SMIAPP_SCALING_MODE_NONE; - } else if (ssd == sensor->binner) { - sensor->binning_horizontal = 1; - sensor->binning_vertical = 1; - } - } - /* Fall through */ - case V4L2_SEL_TGT_COMPOSE: - *crops[SMIAPP_PAD_SRC] = *comp; - break; - default: - BUG(); - } -} - -static const struct smiapp_csi_data_format -*smiapp_validate_csi_data_format(struct smiapp_sensor *sensor, u32 code) -{ - const struct smiapp_csi_data_format *csi_format = sensor->csi_format; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) { - if (sensor->mbus_frame_fmts & (1 << i) - && smiapp_csi_data_formats[i].code == code) - return &smiapp_csi_data_formats[i]; - } - - return csi_format; -} - -static int smiapp_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *fmt) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); - struct v4l2_rect *crops[SMIAPP_PADS]; - - mutex_lock(&sensor->mutex); - - /* - * Media bus code is changeable on src subdev's source pad. On - * other source pads we just get format here. - */ - if (fmt->pad == ssd->source_pad) { - u32 code = fmt->format.code; - int rval = __smiapp_get_format(subdev, fh, fmt); - - if (!rval && subdev == &sensor->src->sd) { - const struct smiapp_csi_data_format *csi_format = - smiapp_validate_csi_data_format(sensor, code); - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) - sensor->csi_format = csi_format; - fmt->format.code = csi_format->code; - } - - mutex_unlock(&sensor->mutex); - return rval; - } - - /* Sink pad. Width and height are changeable here. */ - fmt->format.code = __smiapp_get_mbus_code(subdev, fmt->pad); - fmt->format.width &= ~1; - fmt->format.height &= ~1; - - fmt->format.width = - clamp(fmt->format.width, - sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE], - sensor->limits[SMIAPP_LIMIT_MAX_X_OUTPUT_SIZE]); - fmt->format.height = - clamp(fmt->format.height, - sensor->limits[SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE], - sensor->limits[SMIAPP_LIMIT_MAX_Y_OUTPUT_SIZE]); - - smiapp_get_crop_compose(subdev, fh, crops, NULL, fmt->which); - - crops[ssd->sink_pad]->left = 0; - crops[ssd->sink_pad]->top = 0; - crops[ssd->sink_pad]->width = fmt->format.width; - crops[ssd->sink_pad]->height = fmt->format.height; - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) - ssd->sink_fmt = *crops[ssd->sink_pad]; - smiapp_propagate(subdev, fh, fmt->which, - V4L2_SEL_TGT_CROP); - - mutex_unlock(&sensor->mutex); - - return 0; -} - -/* - * Calculate goodness of scaled image size compared to expected image - * size and flags provided. - */ -#define SCALING_GOODNESS 100000 -#define SCALING_GOODNESS_EXTREME 100000000 -static int scaling_goodness(struct v4l2_subdev *subdev, int w, int ask_w, - int h, int ask_h, u32 flags) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct i2c_client *client = v4l2_get_subdevdata(subdev); - int val = 0; - - w &= ~1; - ask_w &= ~1; - h &= ~1; - ask_h &= ~1; - - if (flags & V4L2_SEL_FLAG_GE) { - if (w < ask_w) - val -= SCALING_GOODNESS; - if (h < ask_h) - val -= SCALING_GOODNESS; - } - - if (flags & V4L2_SEL_FLAG_LE) { - if (w > ask_w) - val -= SCALING_GOODNESS; - if (h > ask_h) - val -= SCALING_GOODNESS; - } - - val -= abs(w - ask_w); - val -= abs(h - ask_h); - - if (w < sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE]) - val -= SCALING_GOODNESS_EXTREME; - - dev_dbg(&client->dev, "w %d ask_w %d h %d ask_h %d goodness %d\n", - w, ask_h, h, ask_h, val); - - return val; -} - -static void smiapp_set_compose_binner(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_selection *sel, - struct v4l2_rect **crops, - struct v4l2_rect *comp) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - unsigned int i; - unsigned int binh = 1, binv = 1; - unsigned int best = scaling_goodness( - subdev, - crops[SMIAPP_PAD_SINK]->width, sel->r.width, - crops[SMIAPP_PAD_SINK]->height, sel->r.height, sel->flags); - - for (i = 0; i < sensor->nbinning_subtypes; i++) { - int this = scaling_goodness( - subdev, - crops[SMIAPP_PAD_SINK]->width - / sensor->binning_subtypes[i].horizontal, - sel->r.width, - crops[SMIAPP_PAD_SINK]->height - / sensor->binning_subtypes[i].vertical, - sel->r.height, sel->flags); - - if (this > best) { - binh = sensor->binning_subtypes[i].horizontal; - binv = sensor->binning_subtypes[i].vertical; - best = this; - } - } - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - sensor->binning_vertical = binv; - sensor->binning_horizontal = binh; - } - - sel->r.width = (crops[SMIAPP_PAD_SINK]->width / binh) & ~1; - sel->r.height = (crops[SMIAPP_PAD_SINK]->height / binv) & ~1; -} - -/* - * Calculate best scaling ratio and mode for given output resolution. - * - * Try all of these: horizontal ratio, vertical ratio and smallest - * size possible (horizontally). - * - * Also try whether horizontal scaler or full scaler gives a better - * result. - */ -static void smiapp_set_compose_scaler(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_selection *sel, - struct v4l2_rect **crops, - struct v4l2_rect *comp) -{ - struct i2c_client *client = v4l2_get_subdevdata(subdev); - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - u32 min, max, a, b, max_m; - u32 scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; - int mode = SMIAPP_SCALING_MODE_HORIZONTAL; - u32 try[4]; - u32 ntry = 0; - unsigned int i; - int best = INT_MIN; - - sel->r.width = min_t(unsigned int, sel->r.width, - crops[SMIAPP_PAD_SINK]->width); - sel->r.height = min_t(unsigned int, sel->r.height, - crops[SMIAPP_PAD_SINK]->height); - - a = crops[SMIAPP_PAD_SINK]->width - * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] / sel->r.width; - b = crops[SMIAPP_PAD_SINK]->height - * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] / sel->r.height; - max_m = crops[SMIAPP_PAD_SINK]->width - * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] - / sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE]; - - a = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX], - max(a, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN])); - b = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX], - max(b, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN])); - max_m = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX], - max(max_m, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN])); - - dev_dbg(&client->dev, "scaling: a %d b %d max_m %d\n", a, b, max_m); - - min = min(max_m, min(a, b)); - max = min(max_m, max(a, b)); - - try[ntry] = min; - ntry++; - if (min != max) { - try[ntry] = max; - ntry++; - } - if (max != max_m) { - try[ntry] = min + 1; - ntry++; - if (min != max) { - try[ntry] = max + 1; - ntry++; - } - } - - for (i = 0; i < ntry; i++) { - int this = scaling_goodness( - subdev, - crops[SMIAPP_PAD_SINK]->width - / try[i] - * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN], - sel->r.width, - crops[SMIAPP_PAD_SINK]->height, - sel->r.height, - sel->flags); - - dev_dbg(&client->dev, "trying factor %d (%d)\n", try[i], i); - - if (this > best) { - scale_m = try[i]; - mode = SMIAPP_SCALING_MODE_HORIZONTAL; - best = this; - } - - if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] - == SMIAPP_SCALING_CAPABILITY_HORIZONTAL) - continue; - - this = scaling_goodness( - subdev, crops[SMIAPP_PAD_SINK]->width - / try[i] - * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN], - sel->r.width, - crops[SMIAPP_PAD_SINK]->height - / try[i] - * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN], - sel->r.height, - sel->flags); - - if (this > best) { - scale_m = try[i]; - mode = SMIAPP_SCALING_MODE_BOTH; - best = this; - } - } - - sel->r.width = - (crops[SMIAPP_PAD_SINK]->width - / scale_m - * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]) & ~1; - if (mode == SMIAPP_SCALING_MODE_BOTH) - sel->r.height = - (crops[SMIAPP_PAD_SINK]->height - / scale_m - * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]) - & ~1; - else - sel->r.height = crops[SMIAPP_PAD_SINK]->height; - - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - sensor->scale_m = scale_m; - sensor->scaling_mode = mode; - } -} -/* We're only called on source pads. This function sets scaling. */ -static int smiapp_set_compose(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_selection *sel) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); - struct v4l2_rect *comp, *crops[SMIAPP_PADS]; - - smiapp_get_crop_compose(subdev, fh, crops, &comp, sel->which); - - sel->r.top = 0; - sel->r.left = 0; - - if (ssd == sensor->binner) - smiapp_set_compose_binner(subdev, fh, sel, crops, comp); - else - smiapp_set_compose_scaler(subdev, fh, sel, crops, comp); - - *comp = sel->r; - smiapp_propagate(subdev, fh, sel->which, - V4L2_SEL_TGT_COMPOSE); - - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return smiapp_update_mode(sensor); - - return 0; -} - -static int __smiapp_sel_supported(struct v4l2_subdev *subdev, - struct v4l2_subdev_selection *sel) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); - - /* We only implement crop in three places. */ - switch (sel->target) { - case V4L2_SEL_TGT_CROP: - case V4L2_SEL_TGT_CROP_BOUNDS: - if (ssd == sensor->pixel_array - && sel->pad == SMIAPP_PA_PAD_SRC) - return 0; - if (ssd == sensor->src - && sel->pad == SMIAPP_PAD_SRC) - return 0; - if (ssd == sensor->scaler - && sel->pad == SMIAPP_PAD_SINK - && sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] - == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) - return 0; - return -EINVAL; - case V4L2_SEL_TGT_COMPOSE: - case V4L2_SEL_TGT_COMPOSE_BOUNDS: - if (sel->pad == ssd->source_pad) - return -EINVAL; - if (ssd == sensor->binner) - return 0; - if (ssd == sensor->scaler - && sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] - != SMIAPP_SCALING_CAPABILITY_NONE) - return 0; - /* Fall through */ - default: - return -EINVAL; - } -} - -static int smiapp_set_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_selection *sel) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); - struct v4l2_rect *src_size, *crops[SMIAPP_PADS]; - struct v4l2_rect _r; - - smiapp_get_crop_compose(subdev, fh, crops, NULL, sel->which); - - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - if (sel->pad == ssd->sink_pad) - src_size = &ssd->sink_fmt; - else - src_size = &ssd->compose; - } else { - if (sel->pad == ssd->sink_pad) { - _r.left = 0; - _r.top = 0; - _r.width = v4l2_subdev_get_try_format(fh, sel->pad) - ->width; - _r.height = v4l2_subdev_get_try_format(fh, sel->pad) - ->height; - src_size = &_r; - } else { - src_size = - v4l2_subdev_get_try_compose( - fh, ssd->sink_pad); - } - } - - if (ssd == sensor->src && sel->pad == SMIAPP_PAD_SRC) { - sel->r.left = 0; - sel->r.top = 0; - } - - sel->r.width = min(sel->r.width, src_size->width); - sel->r.height = min(sel->r.height, src_size->height); - - sel->r.left = min(sel->r.left, src_size->width - sel->r.width); - sel->r.top = min(sel->r.top, src_size->height - sel->r.height); - - *crops[sel->pad] = sel->r; - - if (ssd != sensor->pixel_array && sel->pad == SMIAPP_PAD_SINK) - smiapp_propagate(subdev, fh, sel->which, - V4L2_SEL_TGT_CROP); - - return 0; -} - -static int __smiapp_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_selection *sel) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); - struct v4l2_rect *comp, *crops[SMIAPP_PADS]; - struct v4l2_rect sink_fmt; - int ret; - - ret = __smiapp_sel_supported(subdev, sel); - if (ret) - return ret; - - smiapp_get_crop_compose(subdev, fh, crops, &comp, sel->which); - - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - sink_fmt = ssd->sink_fmt; - } else { - struct v4l2_mbus_framefmt *fmt = - v4l2_subdev_get_try_format(fh, ssd->sink_pad); - - sink_fmt.left = 0; - sink_fmt.top = 0; - sink_fmt.width = fmt->width; - sink_fmt.height = fmt->height; - } - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - if (ssd == sensor->pixel_array) { - sel->r.width = - sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; - sel->r.height = - sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1; - } else if (sel->pad == ssd->sink_pad) { - sel->r = sink_fmt; - } else { - sel->r = *comp; - } - break; - case V4L2_SEL_TGT_CROP: - case V4L2_SEL_TGT_COMPOSE_BOUNDS: - sel->r = *crops[sel->pad]; - break; - case V4L2_SEL_TGT_COMPOSE: - sel->r = *comp; - break; - } - - return 0; -} - -static int smiapp_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_selection *sel) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - int rval; - - mutex_lock(&sensor->mutex); - rval = __smiapp_get_selection(subdev, fh, sel); - mutex_unlock(&sensor->mutex); - - return rval; -} -static int smiapp_set_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_selection *sel) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - int ret; - - ret = __smiapp_sel_supported(subdev, sel); - if (ret) - return ret; - - mutex_lock(&sensor->mutex); - - sel->r.left = max(0, sel->r.left & ~1); - sel->r.top = max(0, sel->r.top & ~1); - sel->r.width = max(0, SMIAPP_ALIGN_DIM(sel->r.width, sel->flags)); - sel->r.height = max(0, SMIAPP_ALIGN_DIM(sel->r.height, sel->flags)); - - sel->r.width = max_t(unsigned int, - sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE], - sel->r.width); - sel->r.height = max_t(unsigned int, - sensor->limits[SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE], - sel->r.height); - - switch (sel->target) { - case V4L2_SEL_TGT_CROP: - ret = smiapp_set_crop(subdev, fh, sel); - break; - case V4L2_SEL_TGT_COMPOSE: - ret = smiapp_set_compose(subdev, fh, sel); - break; - default: - BUG(); - } - - mutex_unlock(&sensor->mutex); - return ret; -} - -static int smiapp_get_skip_frames(struct v4l2_subdev *subdev, u32 *frames) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - - *frames = sensor->frame_skip; - return 0; -} - -/* ----------------------------------------------------------------------------- - * sysfs attributes - */ - -static ssize_t -smiapp_sysfs_nvm_read(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev)); - struct i2c_client *client = v4l2_get_subdevdata(subdev); - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - unsigned int nbytes; - - if (!sensor->dev_init_done) - return -EBUSY; - - if (!sensor->nvm_size) { - /* NVM not read yet - read it now */ - sensor->nvm_size = sensor->platform_data->nvm_size; - if (smiapp_set_power(subdev, 1) < 0) - return -ENODEV; - if (smiapp_read_nvm(sensor, sensor->nvm)) { - dev_err(&client->dev, "nvm read failed\n"); - return -ENODEV; - } - smiapp_set_power(subdev, 0); - } - /* - * NVM is still way below a PAGE_SIZE, so we can safely - * assume this for now. - */ - nbytes = min_t(unsigned int, sensor->nvm_size, PAGE_SIZE); - memcpy(buf, sensor->nvm, nbytes); - - return nbytes; -} -static DEVICE_ATTR(nvm, S_IRUGO, smiapp_sysfs_nvm_read, NULL); - -/* ----------------------------------------------------------------------------- - * V4L2 subdev core operations - */ - -static int smiapp_identify_module(struct v4l2_subdev *subdev) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct i2c_client *client = v4l2_get_subdevdata(subdev); - struct smiapp_module_info *minfo = &sensor->minfo; - unsigned int i; - int rval = 0; - - minfo->name = SMIAPP_NAME; - - /* Module info */ - rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_MANUFACTURER_ID, - &minfo->manufacturer_id); - if (!rval) - rval = smiapp_read_8only(sensor, SMIAPP_REG_U16_MODEL_ID, - &minfo->model_id); - if (!rval) - rval = smiapp_read_8only(sensor, - SMIAPP_REG_U8_REVISION_NUMBER_MAJOR, - &minfo->revision_number_major); - if (!rval) - rval = smiapp_read_8only(sensor, - SMIAPP_REG_U8_REVISION_NUMBER_MINOR, - &minfo->revision_number_minor); - if (!rval) - rval = smiapp_read_8only(sensor, - SMIAPP_REG_U8_MODULE_DATE_YEAR, - &minfo->module_year); - if (!rval) - rval = smiapp_read_8only(sensor, - SMIAPP_REG_U8_MODULE_DATE_MONTH, - &minfo->module_month); - if (!rval) - rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_MODULE_DATE_DAY, - &minfo->module_day); - - /* Sensor info */ - if (!rval) - rval = smiapp_read_8only(sensor, - SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID, - &minfo->sensor_manufacturer_id); - if (!rval) - rval = smiapp_read_8only(sensor, - SMIAPP_REG_U16_SENSOR_MODEL_ID, - &minfo->sensor_model_id); - if (!rval) - rval = smiapp_read_8only(sensor, - SMIAPP_REG_U8_SENSOR_REVISION_NUMBER, - &minfo->sensor_revision_number); - if (!rval) - rval = smiapp_read_8only(sensor, - SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION, - &minfo->sensor_firmware_version); - - /* SMIA */ - if (!rval) - rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_SMIA_VERSION, - &minfo->smia_version); - if (!rval) - rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_SMIAPP_VERSION, - &minfo->smiapp_version); - - if (rval) { - dev_err(&client->dev, "sensor detection failed\n"); - return -ENODEV; - } - - dev_dbg(&client->dev, "module 0x%2.2x-0x%4.4x\n", - minfo->manufacturer_id, minfo->model_id); - - dev_dbg(&client->dev, - "module revision 0x%2.2x-0x%2.2x date %2.2d-%2.2d-%2.2d\n", - minfo->revision_number_major, minfo->revision_number_minor, - minfo->module_year, minfo->module_month, minfo->module_day); - - dev_dbg(&client->dev, "sensor 0x%2.2x-0x%4.4x\n", - minfo->sensor_manufacturer_id, minfo->sensor_model_id); - - dev_dbg(&client->dev, - "sensor revision 0x%2.2x firmware version 0x%2.2x\n", - minfo->sensor_revision_number, minfo->sensor_firmware_version); - - dev_dbg(&client->dev, "smia version %2.2d smiapp version %2.2d\n", - minfo->smia_version, minfo->smiapp_version); - - /* - * Some modules have bad data in the lvalues below. Hope the - * rvalues have better stuff. The lvalues are module - * parameters whereas the rvalues are sensor parameters. - */ - if (!minfo->manufacturer_id && !minfo->model_id) { - minfo->manufacturer_id = minfo->sensor_manufacturer_id; - minfo->model_id = minfo->sensor_model_id; - minfo->revision_number_major = minfo->sensor_revision_number; - } - - for (i = 0; i < ARRAY_SIZE(smiapp_module_idents); i++) { - if (smiapp_module_idents[i].manufacturer_id - != minfo->manufacturer_id) - continue; - if (smiapp_module_idents[i].model_id != minfo->model_id) - continue; - if (smiapp_module_idents[i].flags - & SMIAPP_MODULE_IDENT_FLAG_REV_LE) { - if (smiapp_module_idents[i].revision_number_major - < minfo->revision_number_major) - continue; - } else { - if (smiapp_module_idents[i].revision_number_major - != minfo->revision_number_major) - continue; - } - - minfo->name = smiapp_module_idents[i].name; - minfo->quirk = smiapp_module_idents[i].quirk; - break; - } - - if (i >= ARRAY_SIZE(smiapp_module_idents)) - dev_warn(&client->dev, - "no quirks for this module; let's hope it's fully compliant\n"); - - dev_dbg(&client->dev, "the sensor is called %s, ident %2.2x%4.4x%2.2x\n", - minfo->name, minfo->manufacturer_id, minfo->model_id, - minfo->revision_number_major); - - strlcpy(subdev->name, sensor->minfo.name, sizeof(subdev->name)); - - return 0; -} - -static const struct v4l2_subdev_ops smiapp_ops; -static const struct v4l2_subdev_internal_ops smiapp_internal_ops; -static const struct media_entity_operations smiapp_entity_ops; - -static int smiapp_registered(struct v4l2_subdev *subdev) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct i2c_client *client = v4l2_get_subdevdata(subdev); - struct smiapp_subdev *last = NULL; - u32 tmp; - unsigned int i; - int rval; - - sensor->vana = regulator_get(&client->dev, "VANA"); - if (IS_ERR(sensor->vana)) { - dev_err(&client->dev, "could not get regulator for vana\n"); - return -ENODEV; - } - - if (!sensor->platform_data->set_xclk) { - sensor->ext_clk = clk_get(&client->dev, - sensor->platform_data->ext_clk_name); - if (IS_ERR(sensor->ext_clk)) { - dev_err(&client->dev, "could not get clock %s\n", - sensor->platform_data->ext_clk_name); - rval = -ENODEV; - goto out_clk_get; - } - - rval = clk_set_rate(sensor->ext_clk, - sensor->platform_data->ext_clk); - if (rval < 0) { - dev_err(&client->dev, - "unable to set clock %s freq to %u\n", - sensor->platform_data->ext_clk_name, - sensor->platform_data->ext_clk); - rval = -ENODEV; - goto out_clk_set_rate; - } - } - - if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) { - if (gpio_request_one(sensor->platform_data->xshutdown, 0, - "SMIA++ xshutdown") != 0) { - dev_err(&client->dev, - "unable to acquire reset gpio %d\n", - sensor->platform_data->xshutdown); - rval = -ENODEV; - goto out_clk_set_rate; - } - } - - rval = smiapp_power_on(sensor); - if (rval) { - rval = -ENODEV; - goto out_smiapp_power_on; - } - - rval = smiapp_identify_module(subdev); - if (rval) { - rval = -ENODEV; - goto out_power_off; - } - - rval = smiapp_get_all_limits(sensor); - if (rval) { - rval = -ENODEV; - goto out_power_off; - } - - /* - * Handle Sensor Module orientation on the board. - * - * The application of H-FLIP and V-FLIP on the sensor is modified by - * the sensor orientation on the board. - * - * For SMIAPP_BOARD_SENSOR_ORIENT_180 the default behaviour is to set - * both H-FLIP and V-FLIP for normal operation which also implies - * that a set/unset operation for user space HFLIP and VFLIP v4l2 - * controls will need to be internally inverted. - * - * Rotation also changes the bayer pattern. - */ - if (sensor->platform_data->module_board_orient == - SMIAPP_MODULE_BOARD_ORIENT_180) - sensor->hvflip_inv_mask = SMIAPP_IMAGE_ORIENTATION_HFLIP | - SMIAPP_IMAGE_ORIENTATION_VFLIP; - - rval = smiapp_get_mbus_formats(sensor); - if (rval) { - rval = -ENODEV; - goto out_power_off; - } - - if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY]) { - u32 val; - - rval = smiapp_read(sensor, - SMIAPP_REG_U8_BINNING_SUBTYPES, &val); - if (rval < 0) { - rval = -ENODEV; - goto out_power_off; - } - sensor->nbinning_subtypes = min_t(u8, val, - SMIAPP_BINNING_SUBTYPES); - - for (i = 0; i < sensor->nbinning_subtypes; i++) { - rval = smiapp_read( - sensor, SMIAPP_REG_U8_BINNING_TYPE_n(i), &val); - if (rval < 0) { - rval = -ENODEV; - goto out_power_off; - } - sensor->binning_subtypes[i] = - *(struct smiapp_binning_subtype *)&val; - - dev_dbg(&client->dev, "binning %xx%x\n", - sensor->binning_subtypes[i].horizontal, - sensor->binning_subtypes[i].vertical); - } - } - sensor->binning_horizontal = 1; - sensor->binning_vertical = 1; - - /* SMIA++ NVM initialization - it will be read from the sensor - * when it is first requested by userspace. - */ - if (sensor->minfo.smiapp_version && sensor->platform_data->nvm_size) { - sensor->nvm = kzalloc(sensor->platform_data->nvm_size, - GFP_KERNEL); - if (sensor->nvm == NULL) { - dev_err(&client->dev, "nvm buf allocation failed\n"); - rval = -ENOMEM; - goto out_power_off; - } - - if (device_create_file(&client->dev, &dev_attr_nvm) != 0) { - dev_err(&client->dev, "sysfs nvm entry failed\n"); - rval = -EBUSY; - goto out_power_off; - } - } - - rval = smiapp_call_quirk(sensor, limits); - if (rval) { - dev_err(&client->dev, "limits quirks failed\n"); - goto out_nvm_release; - } - - /* We consider this as profile 0 sensor if any of these are zero. */ - if (!sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV] || - !sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV] || - !sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV] || - !sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV]) { - sensor->minfo.smiapp_profile = SMIAPP_PROFILE_0; - } else if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] - != SMIAPP_SCALING_CAPABILITY_NONE) { - if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] - == SMIAPP_SCALING_CAPABILITY_HORIZONTAL) - sensor->minfo.smiapp_profile = SMIAPP_PROFILE_1; - else - sensor->minfo.smiapp_profile = SMIAPP_PROFILE_2; - sensor->scaler = &sensor->ssds[sensor->ssds_used]; - sensor->ssds_used++; - } else if (sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] - == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) { - sensor->scaler = &sensor->ssds[sensor->ssds_used]; - sensor->ssds_used++; - } - sensor->binner = &sensor->ssds[sensor->ssds_used]; - sensor->ssds_used++; - sensor->pixel_array = &sensor->ssds[sensor->ssds_used]; - sensor->ssds_used++; - - sensor->scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; - - for (i = 0; i < SMIAPP_SUBDEVS; i++) { - struct { - struct smiapp_subdev *ssd; - char *name; - } const __this[] = { - { sensor->scaler, "scaler", }, - { sensor->binner, "binner", }, - { sensor->pixel_array, "pixel array", }, - }, *_this = &__this[i]; - struct smiapp_subdev *this = _this->ssd; - - if (!this) - continue; - - if (this != sensor->src) - v4l2_subdev_init(&this->sd, &smiapp_ops); - - this->sensor = sensor; - - if (this == sensor->pixel_array) { - this->npads = 1; - } else { - this->npads = 2; - this->source_pad = 1; - } - - snprintf(this->sd.name, - sizeof(this->sd.name), "%s %s", - sensor->minfo.name, _this->name); - - this->sink_fmt.width = - sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; - this->sink_fmt.height = - sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1; - this->compose.width = this->sink_fmt.width; - this->compose.height = this->sink_fmt.height; - this->crop[this->source_pad] = this->compose; - this->pads[this->source_pad].flags = MEDIA_PAD_FL_SOURCE; - if (this != sensor->pixel_array) { - this->crop[this->sink_pad] = this->compose; - this->pads[this->sink_pad].flags = MEDIA_PAD_FL_SINK; - } - - this->sd.entity.ops = &smiapp_entity_ops; - - if (last == NULL) { - last = this; - continue; - } - - this->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - this->sd.internal_ops = &smiapp_internal_ops; - this->sd.owner = NULL; - v4l2_set_subdevdata(&this->sd, client); - - rval = media_entity_init(&this->sd.entity, - this->npads, this->pads, 0); - if (rval) { - dev_err(&client->dev, - "media_entity_init failed\n"); - goto out_nvm_release; - } - - rval = media_entity_create_link(&this->sd.entity, - this->source_pad, - &last->sd.entity, - last->sink_pad, - MEDIA_LNK_FL_ENABLED | - MEDIA_LNK_FL_IMMUTABLE); - if (rval) { - dev_err(&client->dev, - "media_entity_create_link failed\n"); - goto out_nvm_release; - } - - rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev, - &this->sd); - if (rval) { - dev_err(&client->dev, - "v4l2_device_register_subdev failed\n"); - goto out_nvm_release; - } - - last = this; - } - - dev_dbg(&client->dev, "profile %d\n", sensor->minfo.smiapp_profile); - - sensor->pixel_array->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - - /* final steps */ - smiapp_read_frame_fmt(sensor); - rval = smiapp_init_controls(sensor); - if (rval < 0) - goto out_nvm_release; - - rval = smiapp_update_mode(sensor); - if (rval) { - dev_err(&client->dev, "update mode failed\n"); - goto out_nvm_release; - } - - sensor->streaming = false; - sensor->dev_init_done = true; - - /* check flash capability */ - rval = smiapp_read(sensor, SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, &tmp); - sensor->flash_capability = tmp; - if (rval) - goto out_nvm_release; - - smiapp_power_off(sensor); - - return 0; - -out_nvm_release: - device_remove_file(&client->dev, &dev_attr_nvm); - -out_power_off: - kfree(sensor->nvm); - sensor->nvm = NULL; - smiapp_power_off(sensor); - -out_smiapp_power_on: - if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) - gpio_free(sensor->platform_data->xshutdown); - -out_clk_set_rate: - clk_put(sensor->ext_clk); - sensor->ext_clk = NULL; - -out_clk_get: - regulator_put(sensor->vana); - sensor->vana = NULL; - return rval; -} - -static int smiapp_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct smiapp_subdev *ssd = to_smiapp_subdev(sd); - struct smiapp_sensor *sensor = ssd->sensor; - u32 mbus_code = - smiapp_csi_data_formats[smiapp_pixel_order(sensor)].code; - unsigned int i; - - mutex_lock(&sensor->mutex); - - for (i = 0; i < ssd->npads; i++) { - struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(fh, i); - struct v4l2_rect *try_crop = v4l2_subdev_get_try_crop(fh, i); - struct v4l2_rect *try_comp; - - try_fmt->width = sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; - try_fmt->height = sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1; - try_fmt->code = mbus_code; - - try_crop->top = 0; - try_crop->left = 0; - try_crop->width = try_fmt->width; - try_crop->height = try_fmt->height; - - if (ssd != sensor->pixel_array) - continue; - - try_comp = v4l2_subdev_get_try_compose(fh, i); - *try_comp = *try_crop; - } - - mutex_unlock(&sensor->mutex); - - return smiapp_set_power(sd, 1); -} - -static int smiapp_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - return smiapp_set_power(sd, 0); -} - -static const struct v4l2_subdev_video_ops smiapp_video_ops = { - .s_stream = smiapp_set_stream, -}; - -static const struct v4l2_subdev_core_ops smiapp_core_ops = { - .s_power = smiapp_set_power, -}; - -static const struct v4l2_subdev_pad_ops smiapp_pad_ops = { - .enum_mbus_code = smiapp_enum_mbus_code, - .get_fmt = smiapp_get_format, - .set_fmt = smiapp_set_format, - .get_selection = smiapp_get_selection, - .set_selection = smiapp_set_selection, -}; - -static const struct v4l2_subdev_sensor_ops smiapp_sensor_ops = { - .g_skip_frames = smiapp_get_skip_frames, -}; - -static const struct v4l2_subdev_ops smiapp_ops = { - .core = &smiapp_core_ops, - .video = &smiapp_video_ops, - .pad = &smiapp_pad_ops, - .sensor = &smiapp_sensor_ops, -}; - -static const struct media_entity_operations smiapp_entity_ops = { - .link_validate = v4l2_subdev_link_validate, -}; - -static const struct v4l2_subdev_internal_ops smiapp_internal_src_ops = { - .registered = smiapp_registered, - .open = smiapp_open, - .close = smiapp_close, -}; - -static const struct v4l2_subdev_internal_ops smiapp_internal_ops = { - .open = smiapp_open, - .close = smiapp_close, -}; - -/* ----------------------------------------------------------------------------- - * I2C Driver - */ - -#ifdef CONFIG_PM - -static int smiapp_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - bool streaming; - - BUG_ON(mutex_is_locked(&sensor->mutex)); - - if (sensor->power_count == 0) - return 0; - - if (sensor->streaming) - smiapp_stop_streaming(sensor); - - streaming = sensor->streaming; - - smiapp_power_off(sensor); - - /* save state for resume */ - sensor->streaming = streaming; - - return 0; -} - -static int smiapp_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - int rval; - - if (sensor->power_count == 0) - return 0; - - rval = smiapp_power_on(sensor); - if (rval) - return rval; - - if (sensor->streaming) - rval = smiapp_start_streaming(sensor); - - return rval; -} - -#else - -#define smiapp_suspend NULL -#define smiapp_resume NULL - -#endif /* CONFIG_PM */ - -static int smiapp_probe(struct i2c_client *client, - const struct i2c_device_id *devid) -{ - struct smiapp_sensor *sensor; - int rval; - - if (client->dev.platform_data == NULL) - return -ENODEV; - - sensor = kzalloc(sizeof(*sensor), GFP_KERNEL); - if (sensor == NULL) - return -ENOMEM; - - sensor->platform_data = client->dev.platform_data; - mutex_init(&sensor->mutex); - mutex_init(&sensor->power_mutex); - sensor->src = &sensor->ssds[sensor->ssds_used]; - - v4l2_i2c_subdev_init(&sensor->src->sd, client, &smiapp_ops); - sensor->src->sd.internal_ops = &smiapp_internal_src_ops; - sensor->src->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - sensor->src->sensor = sensor; - - sensor->src->pads[0].flags = MEDIA_PAD_FL_SOURCE; - rval = media_entity_init(&sensor->src->sd.entity, 2, - sensor->src->pads, 0); - if (rval < 0) - kfree(sensor); - - return rval; -} - -static int __exit smiapp_remove(struct i2c_client *client) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - unsigned int i; - - if (sensor->power_count) { - if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) - gpio_set_value(sensor->platform_data->xshutdown, 0); - if (sensor->platform_data->set_xclk) - sensor->platform_data->set_xclk(&sensor->src->sd, 0); - else - clk_disable(sensor->ext_clk); - sensor->power_count = 0; - } - - if (sensor->nvm) { - device_remove_file(&client->dev, &dev_attr_nvm); - kfree(sensor->nvm); - } - - for (i = 0; i < sensor->ssds_used; i++) { - media_entity_cleanup(&sensor->ssds[i].sd.entity); - v4l2_device_unregister_subdev(&sensor->ssds[i].sd); - } - smiapp_free_controls(sensor); - if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) - gpio_free(sensor->platform_data->xshutdown); - if (sensor->ext_clk) - clk_put(sensor->ext_clk); - if (sensor->vana) - regulator_put(sensor->vana); - - kfree(sensor); - - return 0; -} - -static const struct i2c_device_id smiapp_id_table[] = { - { SMIAPP_NAME, 0 }, - { }, -}; -MODULE_DEVICE_TABLE(i2c, smiapp_id_table); - -static const struct dev_pm_ops smiapp_pm_ops = { - .suspend = smiapp_suspend, - .resume = smiapp_resume, -}; - -static struct i2c_driver smiapp_i2c_driver = { - .driver = { - .name = SMIAPP_NAME, - .pm = &smiapp_pm_ops, - }, - .probe = smiapp_probe, - .remove = __exit_p(smiapp_remove), - .id_table = smiapp_id_table, -}; - -module_i2c_driver(smiapp_i2c_driver); - -MODULE_AUTHOR("Sakari Ailus "); -MODULE_DESCRIPTION("Generic SMIA/SMIA++ camera module driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/smiapp/smiapp-limits.c b/drivers/media/video/smiapp/smiapp-limits.c deleted file mode 100644 index 0800e095724..00000000000 --- a/drivers/media/video/smiapp/smiapp-limits.c +++ /dev/null @@ -1,132 +0,0 @@ -/* - * drivers/media/video/smiapp/smiapp-limits.c - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include "smiapp.h" - -struct smiapp_reg_limits smiapp_reg_limits[] = { - { SMIAPP_REG_U16_ANALOGUE_GAIN_CAPABILITY, "analogue_gain_capability" }, /* 0 */ - { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MIN, "analogue_gain_code_min" }, - { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MAX, "analogue_gain_code_max" }, - { SMIAPP_REG_U8_THS_ZERO_MIN, "ths_zero_min" }, - { SMIAPP_REG_U8_TCLK_TRAIL_MIN, "tclk_trail_min" }, - { SMIAPP_REG_U16_INTEGRATION_TIME_CAPABILITY, "integration_time_capability" }, /* 5 */ - { SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MIN, "coarse_integration_time_min" }, - { SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MAX_MARGIN, "coarse_integration_time_max_margin" }, - { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN, "fine_integration_time_min" }, - { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN, "fine_integration_time_max_margin" }, - { SMIAPP_REG_U16_DIGITAL_GAIN_CAPABILITY, "digital_gain_capability" }, /* 10 */ - { SMIAPP_REG_U16_DIGITAL_GAIN_MIN, "digital_gain_min" }, - { SMIAPP_REG_U16_DIGITAL_GAIN_MAX, "digital_gain_max" }, - { SMIAPP_REG_F32_MIN_EXT_CLK_FREQ_HZ, "min_ext_clk_freq_hz" }, - { SMIAPP_REG_F32_MAX_EXT_CLK_FREQ_HZ, "max_ext_clk_freq_hz" }, - { SMIAPP_REG_U16_MIN_PRE_PLL_CLK_DIV, "min_pre_pll_clk_div" }, /* 15 */ - { SMIAPP_REG_U16_MAX_PRE_PLL_CLK_DIV, "max_pre_pll_clk_div" }, - { SMIAPP_REG_F32_MIN_PLL_IP_FREQ_HZ, "min_pll_ip_freq_hz" }, - { SMIAPP_REG_F32_MAX_PLL_IP_FREQ_HZ, "max_pll_ip_freq_hz" }, - { SMIAPP_REG_U16_MIN_PLL_MULTIPLIER, "min_pll_multiplier" }, - { SMIAPP_REG_U16_MAX_PLL_MULTIPLIER, "max_pll_multiplier" }, /* 20 */ - { SMIAPP_REG_F32_MIN_PLL_OP_FREQ_HZ, "min_pll_op_freq_hz" }, - { SMIAPP_REG_F32_MAX_PLL_OP_FREQ_HZ, "max_pll_op_freq_hz" }, - { SMIAPP_REG_U16_MIN_VT_SYS_CLK_DIV, "min_vt_sys_clk_div" }, - { SMIAPP_REG_U16_MAX_VT_SYS_CLK_DIV, "max_vt_sys_clk_div" }, - { SMIAPP_REG_F32_MIN_VT_SYS_CLK_FREQ_HZ, "min_vt_sys_clk_freq_hz" }, /* 25 */ - { SMIAPP_REG_F32_MAX_VT_SYS_CLK_FREQ_HZ, "max_vt_sys_clk_freq_hz" }, - { SMIAPP_REG_F32_MIN_VT_PIX_CLK_FREQ_HZ, "min_vt_pix_clk_freq_hz" }, - { SMIAPP_REG_F32_MAX_VT_PIX_CLK_FREQ_HZ, "max_vt_pix_clk_freq_hz" }, - { SMIAPP_REG_U16_MIN_VT_PIX_CLK_DIV, "min_vt_pix_clk_div" }, - { SMIAPP_REG_U16_MAX_VT_PIX_CLK_DIV, "max_vt_pix_clk_div" }, /* 30 */ - { SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES, "min_frame_length_lines" }, - { SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES, "max_frame_length_lines" }, - { SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK, "min_line_length_pck" }, - { SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK, "max_line_length_pck" }, - { SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK, "min_line_blanking_pck" }, /* 35 */ - { SMIAPP_REG_U16_MIN_FRAME_BLANKING_LINES, "min_frame_blanking_lines" }, - { SMIAPP_REG_U8_MIN_LINE_LENGTH_PCK_STEP_SIZE, "min_line_length_pck_step_size" }, - { SMIAPP_REG_U16_MIN_OP_SYS_CLK_DIV, "min_op_sys_clk_div" }, - { SMIAPP_REG_U16_MAX_OP_SYS_CLK_DIV, "max_op_sys_clk_div" }, - { SMIAPP_REG_F32_MIN_OP_SYS_CLK_FREQ_HZ, "min_op_sys_clk_freq_hz" }, /* 40 */ - { SMIAPP_REG_F32_MAX_OP_SYS_CLK_FREQ_HZ, "max_op_sys_clk_freq_hz" }, - { SMIAPP_REG_U16_MIN_OP_PIX_CLK_DIV, "min_op_pix_clk_div" }, - { SMIAPP_REG_U16_MAX_OP_PIX_CLK_DIV, "max_op_pix_clk_div" }, - { SMIAPP_REG_F32_MIN_OP_PIX_CLK_FREQ_HZ, "min_op_pix_clk_freq_hz" }, - { SMIAPP_REG_F32_MAX_OP_PIX_CLK_FREQ_HZ, "max_op_pix_clk_freq_hz" }, /* 45 */ - { SMIAPP_REG_U16_X_ADDR_MIN, "x_addr_min" }, - { SMIAPP_REG_U16_Y_ADDR_MIN, "y_addr_min" }, - { SMIAPP_REG_U16_X_ADDR_MAX, "x_addr_max" }, - { SMIAPP_REG_U16_Y_ADDR_MAX, "y_addr_max" }, - { SMIAPP_REG_U16_MIN_X_OUTPUT_SIZE, "min_x_output_size" }, /* 50 */ - { SMIAPP_REG_U16_MIN_Y_OUTPUT_SIZE, "min_y_output_size" }, - { SMIAPP_REG_U16_MAX_X_OUTPUT_SIZE, "max_x_output_size" }, - { SMIAPP_REG_U16_MAX_Y_OUTPUT_SIZE, "max_y_output_size" }, - { SMIAPP_REG_U16_MIN_EVEN_INC, "min_even_inc" }, - { SMIAPP_REG_U16_MAX_EVEN_INC, "max_even_inc" }, /* 55 */ - { SMIAPP_REG_U16_MIN_ODD_INC, "min_odd_inc" }, - { SMIAPP_REG_U16_MAX_ODD_INC, "max_odd_inc" }, - { SMIAPP_REG_U16_SCALING_CAPABILITY, "scaling_capability" }, - { SMIAPP_REG_U16_SCALER_M_MIN, "scaler_m_min" }, - { SMIAPP_REG_U16_SCALER_M_MAX, "scaler_m_max" }, /* 60 */ - { SMIAPP_REG_U16_SCALER_N_MIN, "scaler_n_min" }, - { SMIAPP_REG_U16_SCALER_N_MAX, "scaler_n_max" }, - { SMIAPP_REG_U16_SPATIAL_SAMPLING_CAPABILITY, "spatial_sampling_capability" }, - { SMIAPP_REG_U8_DIGITAL_CROP_CAPABILITY, "digital_crop_capability" }, - { SMIAPP_REG_U16_COMPRESSION_CAPABILITY, "compression_capability" }, /* 65 */ - { SMIAPP_REG_U8_FIFO_SUPPORT_CAPABILITY, "fifo_support_capability" }, - { SMIAPP_REG_U8_DPHY_CTRL_CAPABILITY, "dphy_ctrl_capability" }, - { SMIAPP_REG_U8_CSI_LANE_MODE_CAPABILITY, "csi_lane_mode_capability" }, - { SMIAPP_REG_U8_CSI_SIGNALLING_MODE_CAPABILITY, "csi_signalling_mode_capability" }, - { SMIAPP_REG_U8_FAST_STANDBY_CAPABILITY, "fast_standby_capability" }, /* 70 */ - { SMIAPP_REG_U8_CCI_ADDRESS_CONTROL_CAPABILITY, "cci_address_control_capability" }, - { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS, "max_per_lane_bitrate_1_lane_mode_mbps" }, - { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS, "max_per_lane_bitrate_2_lane_mode_mbps" }, - { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS, "max_per_lane_bitrate_3_lane_mode_mbps" }, - { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS, "max_per_lane_bitrate_4_lane_mode_mbps" }, /* 75 */ - { SMIAPP_REG_U8_TEMP_SENSOR_CAPABILITY, "temp_sensor_capability" }, - { SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES_BIN, "min_frame_length_lines_bin" }, - { SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES_BIN, "max_frame_length_lines_bin" }, - { SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK_BIN, "min_line_length_pck_bin" }, - { SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK_BIN, "max_line_length_pck_bin" }, /* 80 */ - { SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK_BIN, "min_line_blanking_pck_bin" }, - { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN_BIN, "fine_integration_time_min_bin" }, - { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN, "fine_integration_time_max_margin_bin" }, - { SMIAPP_REG_U8_BINNING_CAPABILITY, "binning_capability" }, - { SMIAPP_REG_U8_BINNING_WEIGHTING_CAPABILITY, "binning_weighting_capability" }, /* 85 */ - { SMIAPP_REG_U8_DATA_TRANSFER_IF_CAPABILITY, "data_transfer_if_capability" }, - { SMIAPP_REG_U8_SHADING_CORRECTION_CAPABILITY, "shading_correction_capability" }, - { SMIAPP_REG_U8_GREEN_IMBALANCE_CAPABILITY, "green_imbalance_capability" }, - { SMIAPP_REG_U8_BLACK_LEVEL_CAPABILITY, "black_level_capability" }, - { SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_CAPABILITY, "module_specific_correction_capability" }, /* 90 */ - { SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY, "defect_correction_capability" }, - { SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY_2, "defect_correction_capability_2" }, - { SMIAPP_REG_U8_EDOF_CAPABILITY, "edof_capability" }, - { SMIAPP_REG_U8_COLOUR_FEEDBACK_CAPABILITY, "colour_feedback_capability" }, - { SMIAPP_REG_U8_ESTIMATION_MODE_CAPABILITY, "estimation_mode_capability" }, /* 95 */ - { SMIAPP_REG_U8_ESTIMATION_ZONE_CAPABILITY, "estimation_zone_capability" }, - { SMIAPP_REG_U16_CAPABILITY_TRDY_MIN, "capability_trdy_min" }, - { SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, "flash_mode_capability" }, - { SMIAPP_REG_U8_ACTUATOR_CAPABILITY, "actuator_capability" }, - { SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_1, "bracketing_lut_capability_1" }, /* 100 */ - { SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_2, "bracketing_lut_capability_2" }, - { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_STEP, "analogue_gain_code_step" }, - { 0, NULL }, -}; diff --git a/drivers/media/video/smiapp/smiapp-limits.h b/drivers/media/video/smiapp/smiapp-limits.h deleted file mode 100644 index 7f4836bb78d..00000000000 --- a/drivers/media/video/smiapp/smiapp-limits.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * drivers/media/video/smiapp/smiapp-limits.h - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#define SMIAPP_LIMIT_ANALOGUE_GAIN_CAPABILITY 0 -#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN 1 -#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX 2 -#define SMIAPP_LIMIT_THS_ZERO_MIN 3 -#define SMIAPP_LIMIT_TCLK_TRAIL_MIN 4 -#define SMIAPP_LIMIT_INTEGRATION_TIME_CAPABILITY 5 -#define SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MIN 6 -#define SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MAX_MARGIN 7 -#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN 8 -#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN 9 -#define SMIAPP_LIMIT_DIGITAL_GAIN_CAPABILITY 10 -#define SMIAPP_LIMIT_DIGITAL_GAIN_MIN 11 -#define SMIAPP_LIMIT_DIGITAL_GAIN_MAX 12 -#define SMIAPP_LIMIT_MIN_EXT_CLK_FREQ_HZ 13 -#define SMIAPP_LIMIT_MAX_EXT_CLK_FREQ_HZ 14 -#define SMIAPP_LIMIT_MIN_PRE_PLL_CLK_DIV 15 -#define SMIAPP_LIMIT_MAX_PRE_PLL_CLK_DIV 16 -#define SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ 17 -#define SMIAPP_LIMIT_MAX_PLL_IP_FREQ_HZ 18 -#define SMIAPP_LIMIT_MIN_PLL_MULTIPLIER 19 -#define SMIAPP_LIMIT_MAX_PLL_MULTIPLIER 20 -#define SMIAPP_LIMIT_MIN_PLL_OP_FREQ_HZ 21 -#define SMIAPP_LIMIT_MAX_PLL_OP_FREQ_HZ 22 -#define SMIAPP_LIMIT_MIN_VT_SYS_CLK_DIV 23 -#define SMIAPP_LIMIT_MAX_VT_SYS_CLK_DIV 24 -#define SMIAPP_LIMIT_MIN_VT_SYS_CLK_FREQ_HZ 25 -#define SMIAPP_LIMIT_MAX_VT_SYS_CLK_FREQ_HZ 26 -#define SMIAPP_LIMIT_MIN_VT_PIX_CLK_FREQ_HZ 27 -#define SMIAPP_LIMIT_MAX_VT_PIX_CLK_FREQ_HZ 28 -#define SMIAPP_LIMIT_MIN_VT_PIX_CLK_DIV 29 -#define SMIAPP_LIMIT_MAX_VT_PIX_CLK_DIV 30 -#define SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES 31 -#define SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES 32 -#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK 33 -#define SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK 34 -#define SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK 35 -#define SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES 36 -#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_STEP_SIZE 37 -#define SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV 38 -#define SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV 39 -#define SMIAPP_LIMIT_MIN_OP_SYS_CLK_FREQ_HZ 40 -#define SMIAPP_LIMIT_MAX_OP_SYS_CLK_FREQ_HZ 41 -#define SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV 42 -#define SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV 43 -#define SMIAPP_LIMIT_MIN_OP_PIX_CLK_FREQ_HZ 44 -#define SMIAPP_LIMIT_MAX_OP_PIX_CLK_FREQ_HZ 45 -#define SMIAPP_LIMIT_X_ADDR_MIN 46 -#define SMIAPP_LIMIT_Y_ADDR_MIN 47 -#define SMIAPP_LIMIT_X_ADDR_MAX 48 -#define SMIAPP_LIMIT_Y_ADDR_MAX 49 -#define SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE 50 -#define SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE 51 -#define SMIAPP_LIMIT_MAX_X_OUTPUT_SIZE 52 -#define SMIAPP_LIMIT_MAX_Y_OUTPUT_SIZE 53 -#define SMIAPP_LIMIT_MIN_EVEN_INC 54 -#define SMIAPP_LIMIT_MAX_EVEN_INC 55 -#define SMIAPP_LIMIT_MIN_ODD_INC 56 -#define SMIAPP_LIMIT_MAX_ODD_INC 57 -#define SMIAPP_LIMIT_SCALING_CAPABILITY 58 -#define SMIAPP_LIMIT_SCALER_M_MIN 59 -#define SMIAPP_LIMIT_SCALER_M_MAX 60 -#define SMIAPP_LIMIT_SCALER_N_MIN 61 -#define SMIAPP_LIMIT_SCALER_N_MAX 62 -#define SMIAPP_LIMIT_SPATIAL_SAMPLING_CAPABILITY 63 -#define SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY 64 -#define SMIAPP_LIMIT_COMPRESSION_CAPABILITY 65 -#define SMIAPP_LIMIT_FIFO_SUPPORT_CAPABILITY 66 -#define SMIAPP_LIMIT_DPHY_CTRL_CAPABILITY 67 -#define SMIAPP_LIMIT_CSI_LANE_MODE_CAPABILITY 68 -#define SMIAPP_LIMIT_CSI_SIGNALLING_MODE_CAPABILITY 69 -#define SMIAPP_LIMIT_FAST_STANDBY_CAPABILITY 70 -#define SMIAPP_LIMIT_CCI_ADDRESS_CONTROL_CAPABILITY 71 -#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS 72 -#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS 73 -#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS 74 -#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS 75 -#define SMIAPP_LIMIT_TEMP_SENSOR_CAPABILITY 76 -#define SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN 77 -#define SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN 78 -#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN 79 -#define SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN 80 -#define SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN 81 -#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN_BIN 82 -#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN 83 -#define SMIAPP_LIMIT_BINNING_CAPABILITY 84 -#define SMIAPP_LIMIT_BINNING_WEIGHTING_CAPABILITY 85 -#define SMIAPP_LIMIT_DATA_TRANSFER_IF_CAPABILITY 86 -#define SMIAPP_LIMIT_SHADING_CORRECTION_CAPABILITY 87 -#define SMIAPP_LIMIT_GREEN_IMBALANCE_CAPABILITY 88 -#define SMIAPP_LIMIT_BLACK_LEVEL_CAPABILITY 89 -#define SMIAPP_LIMIT_MODULE_SPECIFIC_CORRECTION_CAPABILITY 90 -#define SMIAPP_LIMIT_DEFECT_CORRECTION_CAPABILITY 91 -#define SMIAPP_LIMIT_DEFECT_CORRECTION_CAPABILITY_2 92 -#define SMIAPP_LIMIT_EDOF_CAPABILITY 93 -#define SMIAPP_LIMIT_COLOUR_FEEDBACK_CAPABILITY 94 -#define SMIAPP_LIMIT_ESTIMATION_MODE_CAPABILITY 95 -#define SMIAPP_LIMIT_ESTIMATION_ZONE_CAPABILITY 96 -#define SMIAPP_LIMIT_CAPABILITY_TRDY_MIN 97 -#define SMIAPP_LIMIT_FLASH_MODE_CAPABILITY 98 -#define SMIAPP_LIMIT_ACTUATOR_CAPABILITY 99 -#define SMIAPP_LIMIT_BRACKETING_LUT_CAPABILITY_1 100 -#define SMIAPP_LIMIT_BRACKETING_LUT_CAPABILITY_2 101 -#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_STEP 102 -#define SMIAPP_LIMIT_LAST 103 diff --git a/drivers/media/video/smiapp/smiapp-quirk.c b/drivers/media/video/smiapp/smiapp-quirk.c deleted file mode 100644 index 55e87950dce..00000000000 --- a/drivers/media/video/smiapp/smiapp-quirk.c +++ /dev/null @@ -1,306 +0,0 @@ -/* - * drivers/media/video/smiapp/smiapp-quirk.c - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include - -#include "smiapp.h" - -static int smiapp_write_8(struct smiapp_sensor *sensor, u16 reg, u8 val) -{ - return smiapp_write(sensor, (SMIA_REG_8BIT << 16) | reg, val); -} - -static int smiapp_write_8s(struct smiapp_sensor *sensor, - struct smiapp_reg_8 *regs, int len) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - int rval; - - for (; len > 0; len--, regs++) { - rval = smiapp_write_8(sensor, regs->reg, regs->val); - if (rval < 0) { - dev_err(&client->dev, - "error %d writing reg 0x%4.4x, val 0x%2.2x", - rval, regs->reg, regs->val); - return rval; - } - } - - return 0; -} - -void smiapp_replace_limit(struct smiapp_sensor *sensor, - u32 limit, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - - dev_dbg(&client->dev, "quirk: 0x%8.8x \"%s\" = %d, 0x%x\n", - smiapp_reg_limits[limit].addr, - smiapp_reg_limits[limit].what, val, val); - sensor->limits[limit] = val; -} - -int smiapp_replace_limit_at(struct smiapp_sensor *sensor, - u32 reg, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - int i; - - for (i = 0; smiapp_reg_limits[i].addr; i++) { - if ((smiapp_reg_limits[i].addr & 0xffff) != reg) - continue; - - smiapp_replace_limit(sensor, i, val); - - return 0; - } - - dev_dbg(&client->dev, "quirk: bad register 0x%4.4x\n", reg); - - return -EINVAL; -} - -bool smiapp_quirk_reg(struct smiapp_sensor *sensor, - u32 reg, u32 *val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - const struct smia_reg *sreg; - - if (!sensor->minfo.quirk) - return false; - - sreg = sensor->minfo.quirk->regs; - - if (!sreg) - return false; - - while (sreg->type) { - u16 type = reg >> 16; - u16 reg16 = reg; - - if (sreg->type != type || sreg->reg != reg16) { - sreg++; - continue; - } - - switch ((u8)type) { - case SMIA_REG_8BIT: - dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%2.2x\n", - reg, sreg->val); - break; - case SMIA_REG_16BIT: - dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%4.4x\n", - reg, sreg->val); - break; - case SMIA_REG_32BIT: - dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%8.8x\n", - reg, sreg->val); - break; - } - - *val = sreg->val; - - return true; - } - - return false; -} - -static int jt8ew9_limits(struct smiapp_sensor *sensor) -{ - if (sensor->minfo.revision_number_major < 0x03) - sensor->frame_skip = 1; - - /* Below 24 gain doesn't have effect at all, */ - /* but ~59 is needed for full dynamic range */ - smiapp_replace_limit(sensor, SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN, 59); - smiapp_replace_limit( - sensor, SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX, 6000); - - return 0; -} - -static int jt8ew9_post_poweron(struct smiapp_sensor *sensor) -{ - struct smiapp_reg_8 regs[] = { - { 0x30a3, 0xd8 }, /* Output port control : LVDS ports only */ - { 0x30ae, 0x00 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */ - { 0x30af, 0xd0 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */ - { 0x322d, 0x04 }, /* Adjusting Processing Image Size to Scaler Toshiba Recommendation Setting */ - { 0x3255, 0x0f }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */ - { 0x3256, 0x15 }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */ - { 0x3258, 0x70 }, /* Analog Gain Control Toshiba Recommendation Setting */ - { 0x3259, 0x70 }, /* Analog Gain Control Toshiba Recommendation Setting */ - { 0x325f, 0x7c }, /* Analog Gain Control Toshiba Recommendation Setting */ - { 0x3302, 0x06 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ - { 0x3304, 0x00 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ - { 0x3307, 0x22 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ - { 0x3308, 0x8d }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ - { 0x331e, 0x0f }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ - { 0x3320, 0x30 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ - { 0x3321, 0x11 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ - { 0x3322, 0x98 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ - { 0x3323, 0x64 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ - { 0x3325, 0x83 }, /* Read Out Timing Control Toshiba Recommendation Setting */ - { 0x3330, 0x18 }, /* Read Out Timing Control Toshiba Recommendation Setting */ - { 0x333c, 0x01 }, /* Read Out Timing Control Toshiba Recommendation Setting */ - { 0x3345, 0x2f }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ - { 0x33de, 0x38 }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */ - /* Taken from v03. No idea what the rest are. */ - { 0x32e0, 0x05 }, - { 0x32e1, 0x05 }, - { 0x32e2, 0x04 }, - { 0x32e5, 0x04 }, - { 0x32e6, 0x04 }, - - }; - - return smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs)); -} - -const struct smiapp_quirk smiapp_jt8ew9_quirk = { - .limits = jt8ew9_limits, - .post_poweron = jt8ew9_post_poweron, -}; - -static int imx125es_post_poweron(struct smiapp_sensor *sensor) -{ - /* Taken from v02. No idea what the other two are. */ - struct smiapp_reg_8 regs[] = { - /* - * 0x3302: clk during frame blanking: - * 0x00 - HS mode, 0x01 - LP11 - */ - { 0x3302, 0x01 }, - { 0x302d, 0x00 }, - { 0x3b08, 0x8c }, - }; - - return smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs)); -} - -const struct smiapp_quirk smiapp_imx125es_quirk = { - .post_poweron = imx125es_post_poweron, -}; - -static int jt8ev1_limits(struct smiapp_sensor *sensor) -{ - smiapp_replace_limit(sensor, SMIAPP_LIMIT_X_ADDR_MAX, 4271); - smiapp_replace_limit(sensor, - SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN, 184); - - return 0; -} - -static int jt8ev1_post_poweron(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - int rval; - - struct smiapp_reg_8 regs[] = { - { 0x3031, 0xcd }, /* For digital binning (EQ_MONI) */ - { 0x30a3, 0xd0 }, /* FLASH STROBE enable */ - { 0x3237, 0x00 }, /* For control of pulse timing for ADC */ - { 0x3238, 0x43 }, - { 0x3301, 0x06 }, /* For analog bias for sensor */ - { 0x3302, 0x06 }, - { 0x3304, 0x00 }, - { 0x3305, 0x88 }, - { 0x332a, 0x14 }, - { 0x332c, 0x6b }, - { 0x3336, 0x01 }, - { 0x333f, 0x1f }, - { 0x3355, 0x00 }, - { 0x3356, 0x20 }, - { 0x33bf, 0x20 }, /* Adjust the FBC speed */ - { 0x33c9, 0x20 }, - { 0x33ce, 0x30 }, /* Adjust the parameter for logic function */ - { 0x33cf, 0xec }, /* For Black sun */ - { 0x3328, 0x80 }, /* Ugh. No idea what's this. */ - }; - - struct smiapp_reg_8 regs_96[] = { - { 0x30ae, 0x00 }, /* For control of ADC clock */ - { 0x30af, 0xd0 }, - { 0x30b0, 0x01 }, - }; - - rval = smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs)); - if (rval < 0) - return rval; - - switch (sensor->platform_data->ext_clk) { - case 9600000: - return smiapp_write_8s(sensor, regs_96, - ARRAY_SIZE(regs_96)); - default: - dev_warn(&client->dev, "no MSRs for %d Hz ext_clk\n", - sensor->platform_data->ext_clk); - return 0; - } -} - -static int jt8ev1_pre_streamon(struct smiapp_sensor *sensor) -{ - return smiapp_write_8(sensor, 0x3328, 0x00); -} - -static int jt8ev1_post_streamoff(struct smiapp_sensor *sensor) -{ - int rval; - - /* Workaround: allows fast standby to work properly */ - rval = smiapp_write_8(sensor, 0x3205, 0x04); - if (rval < 0) - return rval; - - /* Wait for 1 ms + one line => 2 ms is likely enough */ - usleep_range(2000, 2000); - - /* Restore it */ - rval = smiapp_write_8(sensor, 0x3205, 0x00); - if (rval < 0) - return rval; - - return smiapp_write_8(sensor, 0x3328, 0x80); -} - -const struct smiapp_quirk smiapp_jt8ev1_quirk = { - .limits = jt8ev1_limits, - .post_poweron = jt8ev1_post_poweron, - .pre_streamon = jt8ev1_pre_streamon, - .post_streamoff = jt8ev1_post_streamoff, - .flags = SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE, -}; - -static int tcm8500md_limits(struct smiapp_sensor *sensor) -{ - smiapp_replace_limit(sensor, SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ, 2700000); - - return 0; -} - -const struct smiapp_quirk smiapp_tcm8500md_quirk = { - .limits = tcm8500md_limits, -}; diff --git a/drivers/media/video/smiapp/smiapp-quirk.h b/drivers/media/video/smiapp/smiapp-quirk.h deleted file mode 100644 index f4dcaabaefe..00000000000 --- a/drivers/media/video/smiapp/smiapp-quirk.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * drivers/media/video/smiapp/smiapp-quirk.h - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#ifndef __SMIAPP_QUIRK__ -#define __SMIAPP_QUIRK__ - -struct smiapp_sensor; - -/** - * struct smiapp_quirk - quirks for sensors that deviate from SMIA++ standard - * - * @limits: Replace sensor->limits with values which can't be read from - * sensor registers. Called the first time the sensor is powered up. - * @post_poweron: Called always after the sensor has been fully powered on. - * @pre_streamon: Called just before streaming is enabled. - * @post_streamon: Called right after stopping streaming. - */ -struct smiapp_quirk { - int (*limits)(struct smiapp_sensor *sensor); - int (*post_poweron)(struct smiapp_sensor *sensor); - int (*pre_streamon)(struct smiapp_sensor *sensor); - int (*post_streamoff)(struct smiapp_sensor *sensor); - const struct smia_reg *regs; - unsigned long flags; -}; - -/* op pix clock is for all lanes in total normally */ -#define SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0) -#define SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY (1 << 1) - -struct smiapp_reg_8 { - u16 reg; - u8 val; -}; - -void smiapp_replace_limit(struct smiapp_sensor *sensor, - u32 limit, u32 val); -bool smiapp_quirk_reg(struct smiapp_sensor *sensor, - u32 reg, u32 *val); - -#define SMIAPP_MK_QUIRK_REG(_reg, _val) \ - { \ - .type = (_reg >> 16), \ - .reg = (u16)_reg, \ - .val = _val, \ - } - -#define smiapp_call_quirk(_sensor, _quirk, ...) \ - (_sensor->minfo.quirk && \ - _sensor->minfo.quirk->_quirk ? \ - _sensor->minfo.quirk->_quirk(_sensor, ##__VA_ARGS__) : 0) - -#define smiapp_needs_quirk(_sensor, _quirk) \ - (_sensor->minfo.quirk ? \ - _sensor->minfo.quirk->flags & _quirk : 0) - -extern const struct smiapp_quirk smiapp_jt8ev1_quirk; -extern const struct smiapp_quirk smiapp_imx125es_quirk; -extern const struct smiapp_quirk smiapp_jt8ew9_quirk; -extern const struct smiapp_quirk smiapp_tcm8500md_quirk; - -#endif /* __SMIAPP_QUIRK__ */ diff --git a/drivers/media/video/smiapp/smiapp-reg-defs.h b/drivers/media/video/smiapp/smiapp-reg-defs.h deleted file mode 100644 index a089eb8161e..00000000000 --- a/drivers/media/video/smiapp/smiapp-reg-defs.h +++ /dev/null @@ -1,503 +0,0 @@ -/* - * drivers/media/video/smiapp/smiapp-reg-defs.h - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ -#define SMIAPP_REG_MK_U8(r) ((SMIA_REG_8BIT << 16) | (r)) -#define SMIAPP_REG_MK_U16(r) ((SMIA_REG_16BIT << 16) | (r)) -#define SMIAPP_REG_MK_U32(r) ((SMIA_REG_32BIT << 16) | (r)) - -#define SMIAPP_REG_MK_F32(r) (SMIA_REG_FLAG_FLOAT | (SMIA_REG_32BIT << 16) | (r)) - -#define SMIAPP_REG_U16_MODEL_ID SMIAPP_REG_MK_U16(0x0000) -#define SMIAPP_REG_U8_REVISION_NUMBER_MAJOR SMIAPP_REG_MK_U8(0x0002) -#define SMIAPP_REG_U8_MANUFACTURER_ID SMIAPP_REG_MK_U8(0x0003) -#define SMIAPP_REG_U8_SMIA_VERSION SMIAPP_REG_MK_U8(0x0004) -#define SMIAPP_REG_U8_FRAME_COUNT SMIAPP_REG_MK_U8(0x0005) -#define SMIAPP_REG_U8_PIXEL_ORDER SMIAPP_REG_MK_U8(0x0006) -#define SMIAPP_REG_U16_DATA_PEDESTAL SMIAPP_REG_MK_U16(0x0008) -#define SMIAPP_REG_U8_PIXEL_DEPTH SMIAPP_REG_MK_U8(0x000c) -#define SMIAPP_REG_U8_REVISION_NUMBER_MINOR SMIAPP_REG_MK_U8(0x0010) -#define SMIAPP_REG_U8_SMIAPP_VERSION SMIAPP_REG_MK_U8(0x0011) -#define SMIAPP_REG_U8_MODULE_DATE_YEAR SMIAPP_REG_MK_U8(0x0012) -#define SMIAPP_REG_U8_MODULE_DATE_MONTH SMIAPP_REG_MK_U8(0x0013) -#define SMIAPP_REG_U8_MODULE_DATE_DAY SMIAPP_REG_MK_U8(0x0014) -#define SMIAPP_REG_U8_MODULE_DATE_PHASE SMIAPP_REG_MK_U8(0x0015) -#define SMIAPP_REG_U16_SENSOR_MODEL_ID SMIAPP_REG_MK_U16(0x0016) -#define SMIAPP_REG_U8_SENSOR_REVISION_NUMBER SMIAPP_REG_MK_U8(0x0018) -#define SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID SMIAPP_REG_MK_U8(0x0019) -#define SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION SMIAPP_REG_MK_U8(0x001a) -#define SMIAPP_REG_U32_SERIAL_NUMBER SMIAPP_REG_MK_U32(0x001c) -#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE SMIAPP_REG_MK_U8(0x0040) -#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE SMIAPP_REG_MK_U8(0x0041) -#define SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(n) SMIAPP_REG_MK_U16(0x0042 + ((n) << 1)) /* 0 <= n <= 14 */ -#define SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(n) SMIAPP_REG_MK_U32(0x0060 + ((n) << 2)) /* 0 <= n <= 7 */ -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CAPABILITY SMIAPP_REG_MK_U16(0x0080) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MIN SMIAPP_REG_MK_U16(0x0084) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MAX SMIAPP_REG_MK_U16(0x0086) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_STEP SMIAPP_REG_MK_U16(0x0088) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_TYPE SMIAPP_REG_MK_U16(0x008a) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_M0 SMIAPP_REG_MK_U16(0x008c) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_C0 SMIAPP_REG_MK_U16(0x008e) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_M1 SMIAPP_REG_MK_U16(0x0090) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_C1 SMIAPP_REG_MK_U16(0x0092) -#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE SMIAPP_REG_MK_U8(0x00c0) -#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_SUBTYPE SMIAPP_REG_MK_U8(0x00c1) -#define SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(n) SMIAPP_REG_MK_U16(0x00c2 + ((n) << 1)) -#define SMIAPP_REG_U8_MODE_SELECT SMIAPP_REG_MK_U8(0x0100) -#define SMIAPP_REG_U8_IMAGE_ORIENTATION SMIAPP_REG_MK_U8(0x0101) -#define SMIAPP_REG_U8_SOFTWARE_RESET SMIAPP_REG_MK_U8(0x0103) -#define SMIAPP_REG_U8_GROUPED_PARAMETER_HOLD SMIAPP_REG_MK_U8(0x0104) -#define SMIAPP_REG_U8_MASK_CORRUPTED_FRAMES SMIAPP_REG_MK_U8(0x0105) -#define SMIAPP_REG_U8_FAST_STANDBY_CTRL SMIAPP_REG_MK_U8(0x0106) -#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL SMIAPP_REG_MK_U8(0x0107) -#define SMIAPP_REG_U8_2ND_CCI_IF_CONTROL SMIAPP_REG_MK_U8(0x0108) -#define SMIAPP_REG_U8_2ND_CCI_ADDRESS_CONTROL SMIAPP_REG_MK_U8(0x0109) -#define SMIAPP_REG_U8_CSI_CHANNEL_IDENTIFIER SMIAPP_REG_MK_U8(0x0110) -#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE SMIAPP_REG_MK_U8(0x0111) -#define SMIAPP_REG_U16_CSI_DATA_FORMAT SMIAPP_REG_MK_U16(0x0112) -#define SMIAPP_REG_U8_CSI_LANE_MODE SMIAPP_REG_MK_U8(0x0114) -#define SMIAPP_REG_U8_CSI2_10_TO_8_DT SMIAPP_REG_MK_U8(0x0115) -#define SMIAPP_REG_U8_CSI2_10_TO_7_DT SMIAPP_REG_MK_U8(0x0116) -#define SMIAPP_REG_U8_CSI2_10_TO_6_DT SMIAPP_REG_MK_U8(0x0117) -#define SMIAPP_REG_U8_CSI2_12_TO_8_DT SMIAPP_REG_MK_U8(0x0118) -#define SMIAPP_REG_U8_CSI2_12_TO_7_DT SMIAPP_REG_MK_U8(0x0119) -#define SMIAPP_REG_U8_CSI2_12_TO_6_DT SMIAPP_REG_MK_U8(0x011a) -#define SMIAPP_REG_U8_CSI2_14_TO_10_DT SMIAPP_REG_MK_U8(0x011b) -#define SMIAPP_REG_U8_CSI2_14_TO_8_DT SMIAPP_REG_MK_U8(0x011c) -#define SMIAPP_REG_U8_CSI2_16_TO_10_DT SMIAPP_REG_MK_U8(0x011d) -#define SMIAPP_REG_U8_CSI2_16_TO_8_DT SMIAPP_REG_MK_U8(0x011e) -#define SMIAPP_REG_U8_GAIN_MODE SMIAPP_REG_MK_U8(0x0120) -#define SMIAPP_REG_U16_VANA_VOLTAGE SMIAPP_REG_MK_U16(0x0130) -#define SMIAPP_REG_U16_VDIG_VOLTAGE SMIAPP_REG_MK_U16(0x0132) -#define SMIAPP_REG_U16_VIO_VOLTAGE SMIAPP_REG_MK_U16(0x0134) -#define SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ SMIAPP_REG_MK_U16(0x0136) -#define SMIAPP_REG_U8_TEMP_SENSOR_CONTROL SMIAPP_REG_MK_U8(0x0138) -#define SMIAPP_REG_U8_TEMP_SENSOR_MODE SMIAPP_REG_MK_U8(0x0139) -#define SMIAPP_REG_U8_TEMP_SENSOR_OUTPUT SMIAPP_REG_MK_U8(0x013a) -#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME SMIAPP_REG_MK_U16(0x0200) -#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME SMIAPP_REG_MK_U16(0x0202) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL SMIAPP_REG_MK_U16(0x0204) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENR SMIAPP_REG_MK_U16(0x0206) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_RED SMIAPP_REG_MK_U16(0x0208) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_BLUE SMIAPP_REG_MK_U16(0x020a) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENB SMIAPP_REG_MK_U16(0x020c) -#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENR SMIAPP_REG_MK_U16(0x020e) -#define SMIAPP_REG_U16_DIGITAL_GAIN_RED SMIAPP_REG_MK_U16(0x0210) -#define SMIAPP_REG_U16_DIGITAL_GAIN_BLUE SMIAPP_REG_MK_U16(0x0212) -#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENB SMIAPP_REG_MK_U16(0x0214) -#define SMIAPP_REG_U16_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x0300) -#define SMIAPP_REG_U16_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x0302) -#define SMIAPP_REG_U16_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x0304) -#define SMIAPP_REG_U16_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x0306) -#define SMIAPP_REG_U16_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x0308) -#define SMIAPP_REG_U16_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x030a) -#define SMIAPP_REG_U16_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x0340) -#define SMIAPP_REG_U16_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x0342) -#define SMIAPP_REG_U16_X_ADDR_START SMIAPP_REG_MK_U16(0x0344) -#define SMIAPP_REG_U16_Y_ADDR_START SMIAPP_REG_MK_U16(0x0346) -#define SMIAPP_REG_U16_X_ADDR_END SMIAPP_REG_MK_U16(0x0348) -#define SMIAPP_REG_U16_Y_ADDR_END SMIAPP_REG_MK_U16(0x034a) -#define SMIAPP_REG_U16_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x034c) -#define SMIAPP_REG_U16_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x034e) -#define SMIAPP_REG_U16_X_EVEN_INC SMIAPP_REG_MK_U16(0x0380) -#define SMIAPP_REG_U16_X_ODD_INC SMIAPP_REG_MK_U16(0x0382) -#define SMIAPP_REG_U16_Y_EVEN_INC SMIAPP_REG_MK_U16(0x0384) -#define SMIAPP_REG_U16_Y_ODD_INC SMIAPP_REG_MK_U16(0x0386) -#define SMIAPP_REG_U16_SCALING_MODE SMIAPP_REG_MK_U16(0x0400) -#define SMIAPP_REG_U16_SPATIAL_SAMPLING SMIAPP_REG_MK_U16(0x0402) -#define SMIAPP_REG_U16_SCALE_M SMIAPP_REG_MK_U16(0x0404) -#define SMIAPP_REG_U16_SCALE_N SMIAPP_REG_MK_U16(0x0406) -#define SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET SMIAPP_REG_MK_U16(0x0408) -#define SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET SMIAPP_REG_MK_U16(0x040a) -#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH SMIAPP_REG_MK_U16(0x040c) -#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT SMIAPP_REG_MK_U16(0x040e) -#define SMIAPP_REG_U16_COMPRESSION_MODE SMIAPP_REG_MK_U16(0x0500) -#define SMIAPP_REG_U16_TEST_PATTERN_MODE SMIAPP_REG_MK_U16(0x0600) -#define SMIAPP_REG_U16_TEST_DATA_RED SMIAPP_REG_MK_U16(0x0602) -#define SMIAPP_REG_U16_TEST_DATA_GREENR SMIAPP_REG_MK_U16(0x0604) -#define SMIAPP_REG_U16_TEST_DATA_BLUE SMIAPP_REG_MK_U16(0x0606) -#define SMIAPP_REG_U16_TEST_DATA_GREENB SMIAPP_REG_MK_U16(0x0608) -#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_WIDTH SMIAPP_REG_MK_U16(0x060a) -#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_POSITION SMIAPP_REG_MK_U16(0x060c) -#define SMIAPP_REG_U16_VERTICAL_CURSOR_WIDTH SMIAPP_REG_MK_U16(0x060e) -#define SMIAPP_REG_U16_VERTICAL_CURSOR_POSITION SMIAPP_REG_MK_U16(0x0610) -#define SMIAPP_REG_U16_FIFO_WATER_MARK_PIXELS SMIAPP_REG_MK_U16(0x0700) -#define SMIAPP_REG_U8_TCLK_POST SMIAPP_REG_MK_U8(0x0800) -#define SMIAPP_REG_U8_THS_PREPARE SMIAPP_REG_MK_U8(0x0801) -#define SMIAPP_REG_U8_THS_ZERO_MIN SMIAPP_REG_MK_U8(0x0802) -#define SMIAPP_REG_U8_THS_TRAIL SMIAPP_REG_MK_U8(0x0803) -#define SMIAPP_REG_U8_TCLK_TRAIL_MIN SMIAPP_REG_MK_U8(0x0804) -#define SMIAPP_REG_U8_TCLK_PREPARE SMIAPP_REG_MK_U8(0x0805) -#define SMIAPP_REG_U8_TCLK_ZERO SMIAPP_REG_MK_U8(0x0806) -#define SMIAPP_REG_U8_TLPX SMIAPP_REG_MK_U8(0x0807) -#define SMIAPP_REG_U8_DPHY_CTRL SMIAPP_REG_MK_U8(0x0808) -#define SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS SMIAPP_REG_MK_U32(0x0820) -#define SMIAPP_REG_U8_BINNING_MODE SMIAPP_REG_MK_U8(0x0900) -#define SMIAPP_REG_U8_BINNING_TYPE SMIAPP_REG_MK_U8(0x0901) -#define SMIAPP_REG_U8_BINNING_WEIGHTING SMIAPP_REG_MK_U8(0x0902) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL SMIAPP_REG_MK_U8(0x0a00) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS SMIAPP_REG_MK_U8(0x0a01) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT SMIAPP_REG_MK_U8(0x0a02) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 SMIAPP_REG_MK_U8(0x0a04) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_1 SMIAPP_REG_MK_U8(0x0a05) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_2 SMIAPP_REG_MK_U8(0x0a06) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_3 SMIAPP_REG_MK_U8(0x0a07) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_4 SMIAPP_REG_MK_U8(0x0a08) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_5 SMIAPP_REG_MK_U8(0x0a09) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_12 SMIAPP_REG_MK_U8(0x0a10) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_13 SMIAPP_REG_MK_U8(0x0a11) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_14 SMIAPP_REG_MK_U8(0x0a12) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_15 SMIAPP_REG_MK_U8(0x0a13) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_16 SMIAPP_REG_MK_U8(0x0a14) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_17 SMIAPP_REG_MK_U8(0x0a15) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_18 SMIAPP_REG_MK_U8(0x0a16) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_19 SMIAPP_REG_MK_U8(0x0a17) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_20 SMIAPP_REG_MK_U8(0x0a18) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_21 SMIAPP_REG_MK_U8(0x0a19) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_22 SMIAPP_REG_MK_U8(0x0a1a) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_23 SMIAPP_REG_MK_U8(0x0a1b) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_24 SMIAPP_REG_MK_U8(0x0a1c) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_25 SMIAPP_REG_MK_U8(0x0a1d) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_26 SMIAPP_REG_MK_U8(0x0a1e) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_27 SMIAPP_REG_MK_U8(0x0a1f) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_28 SMIAPP_REG_MK_U8(0x0a20) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_29 SMIAPP_REG_MK_U8(0x0a21) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_30 SMIAPP_REG_MK_U8(0x0a22) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_31 SMIAPP_REG_MK_U8(0x0a23) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_32 SMIAPP_REG_MK_U8(0x0a24) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_33 SMIAPP_REG_MK_U8(0x0a25) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_34 SMIAPP_REG_MK_U8(0x0a26) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_35 SMIAPP_REG_MK_U8(0x0a27) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_36 SMIAPP_REG_MK_U8(0x0a28) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_37 SMIAPP_REG_MK_U8(0x0a29) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_38 SMIAPP_REG_MK_U8(0x0a2a) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_39 SMIAPP_REG_MK_U8(0x0a2b) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_40 SMIAPP_REG_MK_U8(0x0a2c) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_41 SMIAPP_REG_MK_U8(0x0a2d) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_42 SMIAPP_REG_MK_U8(0x0a2e) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_43 SMIAPP_REG_MK_U8(0x0a2f) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_44 SMIAPP_REG_MK_U8(0x0a30) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_45 SMIAPP_REG_MK_U8(0x0a31) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_46 SMIAPP_REG_MK_U8(0x0a32) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_47 SMIAPP_REG_MK_U8(0x0a33) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_48 SMIAPP_REG_MK_U8(0x0a34) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_49 SMIAPP_REG_MK_U8(0x0a35) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_50 SMIAPP_REG_MK_U8(0x0a36) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_51 SMIAPP_REG_MK_U8(0x0a37) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_52 SMIAPP_REG_MK_U8(0x0a38) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_53 SMIAPP_REG_MK_U8(0x0a39) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_54 SMIAPP_REG_MK_U8(0x0a3a) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_55 SMIAPP_REG_MK_U8(0x0a3b) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_56 SMIAPP_REG_MK_U8(0x0a3c) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_57 SMIAPP_REG_MK_U8(0x0a3d) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_58 SMIAPP_REG_MK_U8(0x0a3e) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_59 SMIAPP_REG_MK_U8(0x0a3f) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_60 SMIAPP_REG_MK_U8(0x0a40) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_61 SMIAPP_REG_MK_U8(0x0a41) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_62 SMIAPP_REG_MK_U8(0x0a42) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_63 SMIAPP_REG_MK_U8(0x0a43) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_CTRL SMIAPP_REG_MK_U8(0x0a44) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_STATUS SMIAPP_REG_MK_U8(0x0a45) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_PAGE_SELECT SMIAPP_REG_MK_U8(0x0a46) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_0 SMIAPP_REG_MK_U8(0x0a48) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_1 SMIAPP_REG_MK_U8(0x0a49) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_2 SMIAPP_REG_MK_U8(0x0a4a) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_3 SMIAPP_REG_MK_U8(0x0a4b) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_4 SMIAPP_REG_MK_U8(0x0a4c) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_5 SMIAPP_REG_MK_U8(0x0a4d) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_6 SMIAPP_REG_MK_U8(0x0a4e) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_7 SMIAPP_REG_MK_U8(0x0a4f) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_8 SMIAPP_REG_MK_U8(0x0a50) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_9 SMIAPP_REG_MK_U8(0x0a51) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_10 SMIAPP_REG_MK_U8(0x0a52) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_11 SMIAPP_REG_MK_U8(0x0a53) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_12 SMIAPP_REG_MK_U8(0x0a54) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_13 SMIAPP_REG_MK_U8(0x0a55) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_14 SMIAPP_REG_MK_U8(0x0a56) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_15 SMIAPP_REG_MK_U8(0x0a57) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_16 SMIAPP_REG_MK_U8(0x0a58) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_17 SMIAPP_REG_MK_U8(0x0a59) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_18 SMIAPP_REG_MK_U8(0x0a5a) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_19 SMIAPP_REG_MK_U8(0x0a5b) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_20 SMIAPP_REG_MK_U8(0x0a5c) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_21 SMIAPP_REG_MK_U8(0x0a5d) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_22 SMIAPP_REG_MK_U8(0x0a5e) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_23 SMIAPP_REG_MK_U8(0x0a5f) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_24 SMIAPP_REG_MK_U8(0x0a60) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_25 SMIAPP_REG_MK_U8(0x0a61) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_26 SMIAPP_REG_MK_U8(0x0a62) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_27 SMIAPP_REG_MK_U8(0x0a63) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_28 SMIAPP_REG_MK_U8(0x0a64) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_29 SMIAPP_REG_MK_U8(0x0a65) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_30 SMIAPP_REG_MK_U8(0x0a66) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_31 SMIAPP_REG_MK_U8(0x0a67) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_32 SMIAPP_REG_MK_U8(0x0a68) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_33 SMIAPP_REG_MK_U8(0x0a69) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_34 SMIAPP_REG_MK_U8(0x0a6a) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_35 SMIAPP_REG_MK_U8(0x0a6b) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_36 SMIAPP_REG_MK_U8(0x0a6c) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_37 SMIAPP_REG_MK_U8(0x0a6d) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_38 SMIAPP_REG_MK_U8(0x0a6e) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_39 SMIAPP_REG_MK_U8(0x0a6f) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_40 SMIAPP_REG_MK_U8(0x0a70) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_41 SMIAPP_REG_MK_U8(0x0a71) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_42 SMIAPP_REG_MK_U8(0x0a72) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_43 SMIAPP_REG_MK_U8(0x0a73) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_44 SMIAPP_REG_MK_U8(0x0a74) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_45 SMIAPP_REG_MK_U8(0x0a75) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_46 SMIAPP_REG_MK_U8(0x0a76) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_47 SMIAPP_REG_MK_U8(0x0a77) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_48 SMIAPP_REG_MK_U8(0x0a78) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_49 SMIAPP_REG_MK_U8(0x0a79) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_50 SMIAPP_REG_MK_U8(0x0a7a) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_51 SMIAPP_REG_MK_U8(0x0a7b) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_52 SMIAPP_REG_MK_U8(0x0a7c) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_53 SMIAPP_REG_MK_U8(0x0a7d) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_54 SMIAPP_REG_MK_U8(0x0a7e) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_55 SMIAPP_REG_MK_U8(0x0a7f) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_56 SMIAPP_REG_MK_U8(0x0a80) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_57 SMIAPP_REG_MK_U8(0x0a81) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_58 SMIAPP_REG_MK_U8(0x0a82) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_59 SMIAPP_REG_MK_U8(0x0a83) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_60 SMIAPP_REG_MK_U8(0x0a84) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_61 SMIAPP_REG_MK_U8(0x0a85) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_62 SMIAPP_REG_MK_U8(0x0a86) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_63 SMIAPP_REG_MK_U8(0x0a87) -#define SMIAPP_REG_U8_SHADING_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b00) -#define SMIAPP_REG_U8_LUMINANCE_CORRECTION_LEVEL SMIAPP_REG_MK_U8(0x0b01) -#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_ENABLE SMIAPP_REG_MK_U8(0x0b02) -#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_WEIGHT SMIAPP_REG_MK_U8(0x0b03) -#define SMIAPP_REG_U8_BLACK_LEVEL_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b04) -#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b05) -#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b06) -#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b07) -#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b08) -#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b09) -#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b0a) -#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b0b) -#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b0c) -#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_WEIGHT SMIAPP_REG_MK_U8(0x0b0d) -#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b0e) -#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b0f) -#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b10) -#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b11) -#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b12) -#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b13) -#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b14) -#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b15) -#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b16) -#define SMIAPP_REG_U8_EDOF_MODE SMIAPP_REG_MK_U8(0x0b80) -#define SMIAPP_REG_U8_SHARPNESS SMIAPP_REG_MK_U8(0x0b83) -#define SMIAPP_REG_U8_DENOISING SMIAPP_REG_MK_U8(0x0b84) -#define SMIAPP_REG_U8_MODULE_SPECIFIC SMIAPP_REG_MK_U8(0x0b85) -#define SMIAPP_REG_U16_DEPTH_OF_FIELD SMIAPP_REG_MK_U16(0x0b86) -#define SMIAPP_REG_U16_FOCUS_DISTANCE SMIAPP_REG_MK_U16(0x0b88) -#define SMIAPP_REG_U8_ESTIMATION_MODE_CTRL SMIAPP_REG_MK_U8(0x0b8a) -#define SMIAPP_REG_U16_COLOUR_TEMPERATURE SMIAPP_REG_MK_U16(0x0b8c) -#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENR SMIAPP_REG_MK_U16(0x0b8e) -#define SMIAPP_REG_U16_ABSOLUTE_GAIN_RED SMIAPP_REG_MK_U16(0x0b90) -#define SMIAPP_REG_U16_ABSOLUTE_GAIN_BLUE SMIAPP_REG_MK_U16(0x0b92) -#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENB SMIAPP_REG_MK_U16(0x0b94) -#define SMIAPP_REG_U8_ESTIMATION_ZONE_MODE SMIAPP_REG_MK_U8(0x0bc0) -#define SMIAPP_REG_U16_FIXED_ZONE_WEIGHTING SMIAPP_REG_MK_U16(0x0bc2) -#define SMIAPP_REG_U16_CUSTOM_ZONE_X_START SMIAPP_REG_MK_U16(0x0bc4) -#define SMIAPP_REG_U16_CUSTOM_ZONE_Y_START SMIAPP_REG_MK_U16(0x0bc6) -#define SMIAPP_REG_U16_CUSTOM_ZONE_WIDTH SMIAPP_REG_MK_U16(0x0bc8) -#define SMIAPP_REG_U16_CUSTOM_ZONE_HEIGHT SMIAPP_REG_MK_U16(0x0bca) -#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL1 SMIAPP_REG_MK_U8(0x0c00) -#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL2 SMIAPP_REG_MK_U8(0x0c01) -#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_1 SMIAPP_REG_MK_U8(0x0c02) -#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_2 SMIAPP_REG_MK_U8(0x0c03) -#define SMIAPP_REG_U16_TRDY_CTRL SMIAPP_REG_MK_U16(0x0c04) -#define SMIAPP_REG_U16_TRDOUT_CTRL SMIAPP_REG_MK_U16(0x0c06) -#define SMIAPP_REG_U16_TSHUTTER_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c08) -#define SMIAPP_REG_U16_TSHUTTER_STROBE_WIDTH_CTRL SMIAPP_REG_MK_U16(0x0c0a) -#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c0c) -#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_CTRL SMIAPP_REG_MK_U16(0x0c0e) -#define SMIAPP_REG_U16_TGRST_INTERVAL_CTRL SMIAPP_REG_MK_U16(0x0c10) -#define SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT SMIAPP_REG_MK_U8(0x0c12) -#define SMIAPP_REG_U16_FLASH_STROBE_START_POINT SMIAPP_REG_MK_U16(0x0c14) -#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL SMIAPP_REG_MK_U16(0x0c16) -#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL SMIAPP_REG_MK_U16(0x0c18) -#define SMIAPP_REG_U8_FLASH_MODE_RS SMIAPP_REG_MK_U8(0x0c1a) -#define SMIAPP_REG_U8_FLASH_TRIGGER_RS SMIAPP_REG_MK_U8(0x0c1b) -#define SMIAPP_REG_U8_FLASH_STATUS SMIAPP_REG_MK_U8(0x0c1c) -#define SMIAPP_REG_U8_SA_STROBE_MODE SMIAPP_REG_MK_U8(0x0c1d) -#define SMIAPP_REG_U16_SA_STROBE_START_POINT SMIAPP_REG_MK_U16(0x0c1e) -#define SMIAPP_REG_U16_TSA_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c20) -#define SMIAPP_REG_U16_TSA_STROBE_WIDTH_CTRL SMIAPP_REG_MK_U16(0x0c22) -#define SMIAPP_REG_U8_SA_STROBE_TRIGGER SMIAPP_REG_MK_U8(0x0c24) -#define SMIAPP_REG_U8_SPECIAL_ACTUATOR_STATUS SMIAPP_REG_MK_U8(0x0c25) -#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_RS_CTRL SMIAPP_REG_MK_U16(0x0c26) -#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_RS_CTRL SMIAPP_REG_MK_U16(0x0c28) -#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_RS_CTRL SMIAPP_REG_MK_U8(0x0c2a) -#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_CTRL SMIAPP_REG_MK_U8(0x0c2b) -#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_CTRL SMIAPP_REG_MK_U16(0x0c2c) -#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_CTRL SMIAPP_REG_MK_U16(0x0c2e) -#define SMIAPP_REG_U8_LOW_LEVEL_CTRL SMIAPP_REG_MK_U8(0x0c80) -#define SMIAPP_REG_U16_MAIN_TRIGGER_REF_POINT SMIAPP_REG_MK_U16(0x0c82) -#define SMIAPP_REG_U16_MAIN_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c84) -#define SMIAPP_REG_U8_MAIN_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c86) -#define SMIAPP_REG_U16_PHASE1_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c88) -#define SMIAPP_REG_U8_PHASE1_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c8a) -#define SMIAPP_REG_U16_PHASE2_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c8c) -#define SMIAPP_REG_U8_PHASE2_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c8e) -#define SMIAPP_REG_U8_MECH_SHUTTER_CTRL SMIAPP_REG_MK_U8(0x0d00) -#define SMIAPP_REG_U8_OPERATION_MODE SMIAPP_REG_MK_U8(0x0d01) -#define SMIAPP_REG_U8_ACT_STATE1 SMIAPP_REG_MK_U8(0x0d02) -#define SMIAPP_REG_U8_ACT_STATE2 SMIAPP_REG_MK_U8(0x0d03) -#define SMIAPP_REG_U16_FOCUS_CHANGE SMIAPP_REG_MK_U16(0x0d80) -#define SMIAPP_REG_U16_FOCUS_CHANGE_CONTROL SMIAPP_REG_MK_U16(0x0d82) -#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE1 SMIAPP_REG_MK_U16(0x0d84) -#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE2 SMIAPP_REG_MK_U16(0x0d86) -#define SMIAPP_REG_U8_STROBE_COUNT_PHASE1 SMIAPP_REG_MK_U8(0x0d88) -#define SMIAPP_REG_U8_STROBE_COUNT_PHASE2 SMIAPP_REG_MK_U8(0x0d89) -#define SMIAPP_REG_U8_POSITION SMIAPP_REG_MK_U8(0x0d8a) -#define SMIAPP_REG_U8_BRACKETING_LUT_CONTROL SMIAPP_REG_MK_U8(0x0e00) -#define SMIAPP_REG_U8_BRACKETING_LUT_MODE SMIAPP_REG_MK_U8(0x0e01) -#define SMIAPP_REG_U8_BRACKETING_LUT_ENTRY_CONTROL SMIAPP_REG_MK_U8(0x0e02) -#define SMIAPP_REG_U8_LUT_PARAMETERS_START SMIAPP_REG_MK_U8(0x0e10) -#define SMIAPP_REG_U8_LUT_PARAMETERS_END SMIAPP_REG_MK_U8(0x0eff) -#define SMIAPP_REG_U16_INTEGRATION_TIME_CAPABILITY SMIAPP_REG_MK_U16(0x1000) -#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MIN SMIAPP_REG_MK_U16(0x1004) -#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MAX_MARGIN SMIAPP_REG_MK_U16(0x1006) -#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN SMIAPP_REG_MK_U16(0x1008) -#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN SMIAPP_REG_MK_U16(0x100a) -#define SMIAPP_REG_U16_DIGITAL_GAIN_CAPABILITY SMIAPP_REG_MK_U16(0x1080) -#define SMIAPP_REG_U16_DIGITAL_GAIN_MIN SMIAPP_REG_MK_U16(0x1084) -#define SMIAPP_REG_U16_DIGITAL_GAIN_MAX SMIAPP_REG_MK_U16(0x1086) -#define SMIAPP_REG_U16_DIGITAL_GAIN_STEP_SIZE SMIAPP_REG_MK_U16(0x1088) -#define SMIAPP_REG_F32_MIN_EXT_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1100) -#define SMIAPP_REG_F32_MAX_EXT_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1104) -#define SMIAPP_REG_U16_MIN_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x1108) -#define SMIAPP_REG_U16_MAX_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x110a) -#define SMIAPP_REG_F32_MIN_PLL_IP_FREQ_HZ SMIAPP_REG_MK_F32(0x110c) -#define SMIAPP_REG_F32_MAX_PLL_IP_FREQ_HZ SMIAPP_REG_MK_F32(0x1110) -#define SMIAPP_REG_U16_MIN_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x1114) -#define SMIAPP_REG_U16_MAX_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x1116) -#define SMIAPP_REG_F32_MIN_PLL_OP_FREQ_HZ SMIAPP_REG_MK_F32(0x1118) -#define SMIAPP_REG_F32_MAX_PLL_OP_FREQ_HZ SMIAPP_REG_MK_F32(0x111c) -#define SMIAPP_REG_U16_MIN_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1120) -#define SMIAPP_REG_U16_MAX_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1122) -#define SMIAPP_REG_F32_MIN_VT_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1124) -#define SMIAPP_REG_F32_MAX_VT_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1128) -#define SMIAPP_REG_F32_MIN_VT_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x112c) -#define SMIAPP_REG_F32_MAX_VT_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1130) -#define SMIAPP_REG_U16_MIN_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x1134) -#define SMIAPP_REG_U16_MAX_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x1136) -#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x1140) -#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x1142) -#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x1144) -#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x1146) -#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK SMIAPP_REG_MK_U16(0x1148) -#define SMIAPP_REG_U16_MIN_FRAME_BLANKING_LINES SMIAPP_REG_MK_U16(0x114a) -#define SMIAPP_REG_U8_MIN_LINE_LENGTH_PCK_STEP_SIZE SMIAPP_REG_MK_U8(0x114c) -#define SMIAPP_REG_U16_MIN_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1160) -#define SMIAPP_REG_U16_MAX_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1162) -#define SMIAPP_REG_F32_MIN_OP_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1164) -#define SMIAPP_REG_F32_MAX_OP_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1168) -#define SMIAPP_REG_U16_MIN_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x116c) -#define SMIAPP_REG_U16_MAX_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x116e) -#define SMIAPP_REG_F32_MIN_OP_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1170) -#define SMIAPP_REG_F32_MAX_OP_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1174) -#define SMIAPP_REG_U16_X_ADDR_MIN SMIAPP_REG_MK_U16(0x1180) -#define SMIAPP_REG_U16_Y_ADDR_MIN SMIAPP_REG_MK_U16(0x1182) -#define SMIAPP_REG_U16_X_ADDR_MAX SMIAPP_REG_MK_U16(0x1184) -#define SMIAPP_REG_U16_Y_ADDR_MAX SMIAPP_REG_MK_U16(0x1186) -#define SMIAPP_REG_U16_MIN_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x1188) -#define SMIAPP_REG_U16_MIN_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118a) -#define SMIAPP_REG_U16_MAX_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118c) -#define SMIAPP_REG_U16_MAX_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118e) -#define SMIAPP_REG_U16_MIN_EVEN_INC SMIAPP_REG_MK_U16(0x11c0) -#define SMIAPP_REG_U16_MAX_EVEN_INC SMIAPP_REG_MK_U16(0x11c2) -#define SMIAPP_REG_U16_MIN_ODD_INC SMIAPP_REG_MK_U16(0x11c4) -#define SMIAPP_REG_U16_MAX_ODD_INC SMIAPP_REG_MK_U16(0x11c6) -#define SMIAPP_REG_U16_SCALING_CAPABILITY SMIAPP_REG_MK_U16(0x1200) -#define SMIAPP_REG_U16_SCALER_M_MIN SMIAPP_REG_MK_U16(0x1204) -#define SMIAPP_REG_U16_SCALER_M_MAX SMIAPP_REG_MK_U16(0x1206) -#define SMIAPP_REG_U16_SCALER_N_MIN SMIAPP_REG_MK_U16(0x1208) -#define SMIAPP_REG_U16_SCALER_N_MAX SMIAPP_REG_MK_U16(0x120a) -#define SMIAPP_REG_U16_SPATIAL_SAMPLING_CAPABILITY SMIAPP_REG_MK_U16(0x120c) -#define SMIAPP_REG_U8_DIGITAL_CROP_CAPABILITY SMIAPP_REG_MK_U8(0x120e) -#define SMIAPP_REG_U16_COMPRESSION_CAPABILITY SMIAPP_REG_MK_U16(0x1300) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINRED SMIAPP_REG_MK_U16(0x1400) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINRED SMIAPP_REG_MK_U16(0x1402) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINRED SMIAPP_REG_MK_U16(0x1404) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINGREEN SMIAPP_REG_MK_U16(0x1406) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINGREEN SMIAPP_REG_MK_U16(0x1408) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINGREEN SMIAPP_REG_MK_U16(0x140a) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINBLUE SMIAPP_REG_MK_U16(0x140c) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINBLUE SMIAPP_REG_MK_U16(0x140e) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINBLUE SMIAPP_REG_MK_U16(0x1410) -#define SMIAPP_REG_U16_FIFO_SIZE_PIXELS SMIAPP_REG_MK_U16(0x1500) -#define SMIAPP_REG_U8_FIFO_SUPPORT_CAPABILITY SMIAPP_REG_MK_U8(0x1502) -#define SMIAPP_REG_U8_DPHY_CTRL_CAPABILITY SMIAPP_REG_MK_U8(0x1600) -#define SMIAPP_REG_U8_CSI_LANE_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1601) -#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1602) -#define SMIAPP_REG_U8_FAST_STANDBY_CAPABILITY SMIAPP_REG_MK_U8(0x1603) -#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL_CAPABILITY SMIAPP_REG_MK_U8(0x1604) -#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1608) -#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x160c) -#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1610) -#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1614) -#define SMIAPP_REG_U8_TEMP_SENSOR_CAPABILITY SMIAPP_REG_MK_U8(0x1618) -#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES_BIN SMIAPP_REG_MK_U16(0x1700) -#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES_BIN SMIAPP_REG_MK_U16(0x1702) -#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK_BIN SMIAPP_REG_MK_U16(0x1704) -#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK_BIN SMIAPP_REG_MK_U16(0x1706) -#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK_BIN SMIAPP_REG_MK_U16(0x1708) -#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN_BIN SMIAPP_REG_MK_U16(0x170a) -#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN SMIAPP_REG_MK_U16(0x170c) -#define SMIAPP_REG_U8_BINNING_CAPABILITY SMIAPP_REG_MK_U8(0x1710) -#define SMIAPP_REG_U8_BINNING_WEIGHTING_CAPABILITY SMIAPP_REG_MK_U8(0x1711) -#define SMIAPP_REG_U8_BINNING_SUBTYPES SMIAPP_REG_MK_U8(0x1712) -#define SMIAPP_REG_U8_BINNING_TYPE_n(n) SMIAPP_REG_MK_U8(0x1713 + (n)) /* 1 <= n <= 237 */ -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_CAPABILITY SMIAPP_REG_MK_U8(0x1800) -#define SMIAPP_REG_U8_SHADING_CORRECTION_CAPABILITY SMIAPP_REG_MK_U8(0x1900) -#define SMIAPP_REG_U8_GREEN_IMBALANCE_CAPABILITY SMIAPP_REG_MK_U8(0x1901) -#define SMIAPP_REG_U8_BLACK_LEVEL_CAPABILITY SMIAPP_REG_MK_U8(0x1902) -#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_CAPABILITY SMIAPP_REG_MK_U8(0x1903) -#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY SMIAPP_REG_MK_U16(0x1904) -#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY_2 SMIAPP_REG_MK_U16(0x1906) -#define SMIAPP_REG_U8_EDOF_CAPABILITY SMIAPP_REG_MK_U8(0x1980) -#define SMIAPP_REG_U8_ESTIMATION_FRAMES SMIAPP_REG_MK_U8(0x1981) -#define SMIAPP_REG_U8_SUPPORTS_SHARPNESS_ADJ SMIAPP_REG_MK_U8(0x1982) -#define SMIAPP_REG_U8_SUPPORTS_DENOISING_ADJ SMIAPP_REG_MK_U8(0x1983) -#define SMIAPP_REG_U8_SUPPORTS_MODULE_SPECIFIC_ADJ SMIAPP_REG_MK_U8(0x1984) -#define SMIAPP_REG_U8_SUPPORTS_DEPTH_OF_FIELD_ADJ SMIAPP_REG_MK_U8(0x1985) -#define SMIAPP_REG_U8_SUPPORTS_FOCUS_DISTANCE_ADJ SMIAPP_REG_MK_U8(0x1986) -#define SMIAPP_REG_U8_COLOUR_FEEDBACK_CAPABILITY SMIAPP_REG_MK_U8(0x1987) -#define SMIAPP_REG_U8_EDOF_SUPPORT_AB_NXM SMIAPP_REG_MK_U8(0x1988) -#define SMIAPP_REG_U8_ESTIMATION_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x19c0) -#define SMIAPP_REG_U8_ESTIMATION_ZONE_CAPABILITY SMIAPP_REG_MK_U8(0x19c1) -#define SMIAPP_REG_U16_EST_DEPTH_OF_FIELD SMIAPP_REG_MK_U16(0x19c2) -#define SMIAPP_REG_U16_EST_FOCUS_DISTANCE SMIAPP_REG_MK_U16(0x19c4) -#define SMIAPP_REG_U16_CAPABILITY_TRDY_MIN SMIAPP_REG_MK_U16(0x1a00) -#define SMIAPP_REG_U8_FLASH_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1a02) -#define SMIAPP_REG_U16_MECH_SHUT_AND_ACT_START_ADDR SMIAPP_REG_MK_U16(0x1b02) -#define SMIAPP_REG_U8_ACTUATOR_CAPABILITY SMIAPP_REG_MK_U8(0x1b04) -#define SMIAPP_REG_U16_ACTUATOR_TYPE SMIAPP_REG_MK_U16(0x1b40) -#define SMIAPP_REG_U8_AF_DEVICE_ADDRESS SMIAPP_REG_MK_U8(0x1b42) -#define SMIAPP_REG_U16_FOCUS_CHANGE_ADDRESS SMIAPP_REG_MK_U16(0x1b44) -#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_1 SMIAPP_REG_MK_U8(0x1c00) -#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_2 SMIAPP_REG_MK_U8(0x1c01) -#define SMIAPP_REG_U8_BRACKETING_LUT_SIZE SMIAPP_REG_MK_U8(0x1c02) diff --git a/drivers/media/video/smiapp/smiapp-reg.h b/drivers/media/video/smiapp/smiapp-reg.h deleted file mode 100644 index d0167aa1753..00000000000 --- a/drivers/media/video/smiapp/smiapp-reg.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * drivers/media/video/smiapp/smiapp-reg.h - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#ifndef __SMIAPP_REG_H_ -#define __SMIAPP_REG_H_ - -#include "smiapp-reg-defs.h" - -/* Bits for above register */ -#define SMIAPP_IMAGE_ORIENTATION_HFLIP (1 << 0) -#define SMIAPP_IMAGE_ORIENTATION_VFLIP (1 << 1) - -#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN (1 << 0) -#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN (0 << 1) -#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_WR_EN (1 << 1) -#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_ERR_CLEAR (1 << 2) -#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY (1 << 0) -#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_WR_READY (1 << 1) -#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EDATA (1 << 2) -#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EUSAGE (1 << 3) - -#define SMIAPP_SOFTWARE_RESET (1 << 0) - -#define SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE (1 << 0) -#define SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE (1 << 1) - -#define SMIAPP_DPHY_CTRL_AUTOMATIC 0 -/* DPHY control based on REQUESTED_LINK_BIT_RATE_MBPS */ -#define SMIAPP_DPHY_CTRL_UI 1 -#define SMIAPP_DPHY_CTRL_REGISTER 2 - -#define SMIAPP_COMPRESSION_MODE_SIMPLE_PREDICTOR 1 -#define SMIAPP_COMPRESSION_MODE_ADVANCED_PREDICTOR 2 - -#define SMIAPP_MODE_SELECT_SOFTWARE_STANDBY 0 -#define SMIAPP_MODE_SELECT_STREAMING 1 - -#define SMIAPP_SCALING_MODE_NONE 0 -#define SMIAPP_SCALING_MODE_HORIZONTAL 1 -#define SMIAPP_SCALING_MODE_BOTH 2 - -#define SMIAPP_SCALING_CAPABILITY_NONE 0 -#define SMIAPP_SCALING_CAPABILITY_HORIZONTAL 1 -#define SMIAPP_SCALING_CAPABILITY_BOTH 2 /* horizontal/both */ - -/* digital crop right before scaler */ -#define SMIAPP_DIGITAL_CROP_CAPABILITY_NONE 0 -#define SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP 1 - -#define SMIAPP_BINNING_CAPABILITY_NO 0 -#define SMIAPP_BINNING_CAPABILITY_YES 1 - -/* Maximum number of binning subtypes */ -#define SMIAPP_BINNING_SUBTYPES 253 - -#define SMIAPP_PIXEL_ORDER_GRBG 0 -#define SMIAPP_PIXEL_ORDER_RGGB 1 -#define SMIAPP_PIXEL_ORDER_BGGR 2 -#define SMIAPP_PIXEL_ORDER_GBRG 3 - -#define SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL 1 -#define SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED 2 -#define SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL_N 8 -#define SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED_N 16 - -#define SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE 0x01 -#define SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE 0x02 -#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NROWS_MASK 0x0f -#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_MASK 0xf0 -#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_SHIFT 4 - -#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_MASK 0xf000 -#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_SHIFT 12 -#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELS_MASK 0x0fff - -#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_MASK 0xf0000000 -#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_SHIFT 28 -#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELS_MASK 0x0000ffff - -#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED 1 -#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DUMMY 2 -#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_BLACK 3 -#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DARK 4 -#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE 5 - -#define SMIAPP_FAST_STANDBY_CTRL_COMPLETE_FRAMES 0 -#define SMIAPP_FAST_STANDBY_CTRL_IMMEDIATE 1 - -/* Scaling N factor */ -#define SMIAPP_SCALE_N 16 - -/* Image statistics registers */ -/* Registers 0x2000 to 0x2fff are reserved for future - * use for statistics features. - */ - -/* Manufacturer Specific Registers: 0x3000 to 0x3fff - * The manufacturer specifies these as a black box. - */ - -#endif /* __SMIAPP_REG_H_ */ diff --git a/drivers/media/video/smiapp/smiapp-regs.c b/drivers/media/video/smiapp/smiapp-regs.c deleted file mode 100644 index b1812b17a40..00000000000 --- a/drivers/media/video/smiapp/smiapp-regs.c +++ /dev/null @@ -1,273 +0,0 @@ -/* - * drivers/media/video/smiapp/smiapp-regs.c - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include -#include - -#include "smiapp.h" -#include "smiapp-regs.h" - -static uint32_t float_to_u32_mul_1000000(struct i2c_client *client, - uint32_t phloat) -{ - int32_t exp; - uint64_t man; - - if (phloat >= 0x80000000) { - dev_err(&client->dev, "this is a negative number\n"); - return 0; - } - - if (phloat == 0x7f800000) - return ~0; /* Inf. */ - - if ((phloat & 0x7f800000) == 0x7f800000) { - dev_err(&client->dev, "NaN or other special number\n"); - return 0; - } - - /* Valid cases begin here */ - if (phloat == 0) - return 0; /* Valid zero */ - - if (phloat > 0x4f800000) - return ~0; /* larger than 4294967295 */ - - /* - * Unbias exponent (note how phloat is now guaranteed to - * have 0 in the high bit) - */ - exp = ((int32_t)phloat >> 23) - 127; - - /* Extract mantissa, add missing '1' bit and it's in MHz */ - man = ((phloat & 0x7fffff) | 0x800000) * 1000000ULL; - - if (exp < 0) - man >>= -exp; - else - man <<= exp; - - man >>= 23; /* Remove mantissa bias */ - - return man & 0xffffffff; -} - - -/* - * Read a 8/16/32-bit i2c register. The value is returned in 'val'. - * Returns zero if successful, or non-zero otherwise. - */ -static int ____smiapp_read(struct smiapp_sensor *sensor, u16 reg, - u16 len, u32 *val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - struct i2c_msg msg; - unsigned char data[4]; - u16 offset = reg; - int r; - - msg.addr = client->addr; - msg.flags = 0; - msg.len = 2; - msg.buf = data; - - /* high byte goes out first */ - data[0] = (u8) (offset >> 8); - data[1] = (u8) offset; - r = i2c_transfer(client->adapter, &msg, 1); - if (r != 1) { - if (r >= 0) - r = -EBUSY; - goto err; - } - - msg.len = len; - msg.flags = I2C_M_RD; - r = i2c_transfer(client->adapter, &msg, 1); - if (r != 1) { - if (r >= 0) - r = -EBUSY; - goto err; - } - - *val = 0; - /* high byte comes first */ - switch (len) { - case SMIA_REG_32BIT: - *val = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + - data[3]; - break; - case SMIA_REG_16BIT: - *val = (data[0] << 8) + data[1]; - break; - case SMIA_REG_8BIT: - *val = data[0]; - break; - default: - BUG(); - } - - return 0; - -err: - dev_err(&client->dev, "read from offset 0x%x error %d\n", offset, r); - - return r; -} - -/* Read a register using 8-bit access only. */ -static int ____smiapp_read_8only(struct smiapp_sensor *sensor, u16 reg, - u16 len, u32 *val) -{ - unsigned int i; - int rval; - - *val = 0; - - for (i = 0; i < len; i++) { - u32 val8; - - rval = ____smiapp_read(sensor, reg + i, 1, &val8); - if (rval < 0) - return rval; - *val |= val8 << ((len - i - 1) << 3); - } - - return 0; -} - -/* - * Read a 8/16/32-bit i2c register. The value is returned in 'val'. - * Returns zero if successful, or non-zero otherwise. - */ -static int __smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val, - bool only8) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - unsigned int len = (u8)(reg >> 16); - int rval; - - if (len != SMIA_REG_8BIT && len != SMIA_REG_16BIT - && len != SMIA_REG_32BIT) - return -EINVAL; - - if (smiapp_quirk_reg(sensor, reg, val)) - goto found_quirk; - - if (len == SMIA_REG_8BIT && !only8) - rval = ____smiapp_read(sensor, (u16)reg, len, val); - else - rval = ____smiapp_read_8only(sensor, (u16)reg, len, val); - if (rval < 0) - return rval; - -found_quirk: - if (reg & SMIA_REG_FLAG_FLOAT) - *val = float_to_u32_mul_1000000(client, *val); - - return 0; -} - -int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val) -{ - return __smiapp_read( - sensor, reg, val, - smiapp_needs_quirk(sensor, - SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY)); -} - -int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val) -{ - return __smiapp_read(sensor, reg, val, true); -} - -/* - * Write to a 8/16-bit register. - * Returns zero if successful, or non-zero otherwise. - */ -int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - struct i2c_msg msg; - unsigned char data[6]; - unsigned int retries; - unsigned int flags = reg >> 24; - unsigned int len = (u8)(reg >> 16); - u16 offset = reg; - int r; - - if ((len != SMIA_REG_8BIT && len != SMIA_REG_16BIT && - len != SMIA_REG_32BIT) || flags) - return -EINVAL; - - msg.addr = client->addr; - msg.flags = 0; /* Write */ - msg.len = 2 + len; - msg.buf = data; - - /* high byte goes out first */ - data[0] = (u8) (reg >> 8); - data[1] = (u8) (reg & 0xff); - - switch (len) { - case SMIA_REG_8BIT: - data[2] = val; - break; - case SMIA_REG_16BIT: - data[2] = val >> 8; - data[3] = val; - break; - case SMIA_REG_32BIT: - data[2] = val >> 24; - data[3] = val >> 16; - data[4] = val >> 8; - data[5] = val; - break; - default: - BUG(); - } - - for (retries = 0; retries < 5; retries++) { - /* - * Due to unknown reason sensor stops responding. This - * loop is a temporaty solution until the root cause - * is found. - */ - r = i2c_transfer(client->adapter, &msg, 1); - if (r == 1) { - if (retries) - dev_err(&client->dev, - "sensor i2c stall encountered. " - "retries: %d\n", retries); - return 0; - } - - usleep_range(2000, 2000); - } - - dev_err(&client->dev, - "wrote 0x%x to offset 0x%x error %d\n", val, offset, r); - - return r; -} diff --git a/drivers/media/video/smiapp/smiapp-regs.h b/drivers/media/video/smiapp/smiapp-regs.h deleted file mode 100644 index 7f9013b4797..00000000000 --- a/drivers/media/video/smiapp/smiapp-regs.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * include/media/smiapp/smiapp-regs.h - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#ifndef SMIAPP_REGS_H -#define SMIAPP_REGS_H - -#include -#include - -/* Use upper 8 bits of the type field for flags */ -#define SMIA_REG_FLAG_FLOAT (1 << 24) - -#define SMIA_REG_8BIT 1 -#define SMIA_REG_16BIT 2 -#define SMIA_REG_32BIT 4 -struct smia_reg { - u16 type; - u16 reg; /* 16-bit offset */ - u32 val; /* 8/16/32-bit value */ -}; - -struct smiapp_sensor; - -int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val); -int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val); -int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val); - -#endif diff --git a/drivers/media/video/smiapp/smiapp.h b/drivers/media/video/smiapp/smiapp.h deleted file mode 100644 index 587f7f11238..00000000000 --- a/drivers/media/video/smiapp/smiapp.h +++ /dev/null @@ -1,252 +0,0 @@ -/* - * drivers/media/video/smiapp/smiapp.h - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2010--2012 Nokia Corporation - * Contact: Sakari Ailus - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#ifndef __SMIAPP_PRIV_H_ -#define __SMIAPP_PRIV_H_ - -#include -#include -#include -#include - -#include "smiapp-pll.h" -#include "smiapp-reg.h" -#include "smiapp-regs.h" -#include "smiapp-quirk.h" - -/* - * Standard SMIA++ constants - */ -#define SMIA_VERSION_1 10 -#define SMIAPP_VERSION_0_8 8 /* Draft 0.8 */ -#define SMIAPP_VERSION_0_9 9 /* Draft 0.9 */ -#define SMIAPP_VERSION_1 10 - -#define SMIAPP_PROFILE_0 0 -#define SMIAPP_PROFILE_1 1 -#define SMIAPP_PROFILE_2 2 - -#define SMIAPP_NVM_PAGE_SIZE 64 /* bytes */ - -#define SMIAPP_RESET_DELAY_CLOCKS 2400 -#define SMIAPP_RESET_DELAY(clk) \ - (1000 + (SMIAPP_RESET_DELAY_CLOCKS * 1000 \ - + (clk) / 1000 - 1) / ((clk) / 1000)) - -#include "smiapp-limits.h" - -struct smiapp_quirk; - -#define SMIAPP_MODULE_IDENT_FLAG_REV_LE (1 << 0) - -struct smiapp_module_ident { - u8 manufacturer_id; - u16 model_id; - u8 revision_number_major; - - u8 flags; - - char *name; - const struct smiapp_quirk *quirk; -}; - -struct smiapp_module_info { - u32 manufacturer_id; - u32 model_id; - u32 revision_number_major; - u32 revision_number_minor; - - u32 module_year; - u32 module_month; - u32 module_day; - - u32 sensor_manufacturer_id; - u32 sensor_model_id; - u32 sensor_revision_number; - u32 sensor_firmware_version; - - u32 smia_version; - u32 smiapp_version; - - u32 smiapp_profile; - - char *name; - const struct smiapp_quirk *quirk; -}; - -#define SMIAPP_IDENT_FQ(manufacturer, model, rev, fl, _name, _quirk) \ - { .manufacturer_id = manufacturer, \ - .model_id = model, \ - .revision_number_major = rev, \ - .flags = fl, \ - .name = _name, \ - .quirk = _quirk, } - -#define SMIAPP_IDENT_LQ(manufacturer, model, rev, _name, _quirk) \ - { .manufacturer_id = manufacturer, \ - .model_id = model, \ - .revision_number_major = rev, \ - .flags = SMIAPP_MODULE_IDENT_FLAG_REV_LE, \ - .name = _name, \ - .quirk = _quirk, } - -#define SMIAPP_IDENT_L(manufacturer, model, rev, _name) \ - { .manufacturer_id = manufacturer, \ - .model_id = model, \ - .revision_number_major = rev, \ - .flags = SMIAPP_MODULE_IDENT_FLAG_REV_LE, \ - .name = _name, } - -#define SMIAPP_IDENT_Q(manufacturer, model, rev, _name, _quirk) \ - { .manufacturer_id = manufacturer, \ - .model_id = model, \ - .revision_number_major = rev, \ - .flags = 0, \ - .name = _name, \ - .quirk = _quirk, } - -#define SMIAPP_IDENT(manufacturer, model, rev, _name) \ - { .manufacturer_id = manufacturer, \ - .model_id = model, \ - .revision_number_major = rev, \ - .flags = 0, \ - .name = _name, } - -struct smiapp_reg_limits { - u32 addr; - char *what; -}; - -extern struct smiapp_reg_limits smiapp_reg_limits[]; - -struct smiapp_csi_data_format { - u32 code; - u8 width; - u8 compressed; - u8 pixel_order; -}; - -#define SMIAPP_SUBDEVS 3 - -#define SMIAPP_PA_PAD_SRC 0 -#define SMIAPP_PAD_SINK 0 -#define SMIAPP_PAD_SRC 1 -#define SMIAPP_PADS 2 - -struct smiapp_binning_subtype { - u8 horizontal:4; - u8 vertical:4; -} __packed; - -struct smiapp_subdev { - struct v4l2_subdev sd; - struct media_pad pads[2]; - struct v4l2_rect sink_fmt; - struct v4l2_rect crop[2]; - struct v4l2_rect compose; /* compose on sink */ - unsigned short sink_pad; - unsigned short source_pad; - int npads; - struct smiapp_sensor *sensor; - struct v4l2_ctrl_handler ctrl_handler; -}; - -/* - * struct smiapp_sensor - Main device structure - */ -struct smiapp_sensor { - /* - * "mutex" is used to serialise access to all fields here - * except v4l2_ctrls at the end of the struct. "mutex" is also - * used to serialise access to file handle specific - * information. The exception to this rule is the power_mutex - * below. - */ - struct mutex mutex; - /* - * power_mutex is used to serialise power management related - * activities. Acquiring "mutex" at that time isn't necessary - * since there are no other users anyway. - */ - struct mutex power_mutex; - struct smiapp_subdev ssds[SMIAPP_SUBDEVS]; - u32 ssds_used; - struct smiapp_subdev *src; - struct smiapp_subdev *binner; - struct smiapp_subdev *scaler; - struct smiapp_subdev *pixel_array; - struct smiapp_platform_data *platform_data; - struct regulator *vana; - struct clk *ext_clk; - u32 limits[SMIAPP_LIMIT_LAST]; - u8 nbinning_subtypes; - struct smiapp_binning_subtype binning_subtypes[SMIAPP_BINNING_SUBTYPES]; - u32 mbus_frame_fmts; - const struct smiapp_csi_data_format *csi_format; - const struct smiapp_csi_data_format *internal_csi_format; - u32 default_mbus_frame_fmts; - int default_pixel_order; - - u8 binning_horizontal; - u8 binning_vertical; - - u8 scale_m; - u8 scaling_mode; - - u8 hvflip_inv_mask; /* H/VFLIP inversion due to sensor orientation */ - u8 flash_capability; - u8 frame_skip; - - int power_count; - - bool streaming; - bool dev_init_done; - - u8 *nvm; /* nvm memory buffer */ - unsigned int nvm_size; /* bytes */ - - struct smiapp_module_info minfo; - - struct smiapp_pll pll; - - /* Pixel array controls */ - struct v4l2_ctrl *analog_gain; - struct v4l2_ctrl *exposure; - struct v4l2_ctrl *hflip; - struct v4l2_ctrl *vflip; - struct v4l2_ctrl *vblank; - struct v4l2_ctrl *hblank; - struct v4l2_ctrl *pixel_rate_parray; - /* src controls */ - struct v4l2_ctrl *link_freq; - struct v4l2_ctrl *pixel_rate_csi; -}; - -#define to_smiapp_subdev(_sd) \ - container_of(_sd, struct smiapp_subdev, sd) - -#define to_smiapp_sensor(_sd) \ - (to_smiapp_subdev(_sd)->sensor) - -#endif /* __SMIAPP_PRIV_H_ */ diff --git a/drivers/media/video/sr030pc30.c b/drivers/media/video/sr030pc30.c deleted file mode 100644 index e9d95bda2ab..00000000000 --- a/drivers/media/video/sr030pc30.c +++ /dev/null @@ -1,871 +0,0 @@ -/* - * Driver for SiliconFile SR030PC30 VGA (1/10-Inch) Image Sensor with ISP - * - * Copyright (C) 2010 Samsung Electronics Co., Ltd - * Author: Sylwester Nawrocki, s.nawrocki@samsung.com - * - * Based on original driver authored by Dongsoo Nathaniel Kim - * and HeungJun Kim . - * - * Based on mt9v011 Micron Digital Image Sensor driver - * Copyright (c) 2009 Mauro Carvalho Chehab (mchehab@redhat.com) - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -static int debug; -module_param(debug, int, 0644); - -#define MODULE_NAME "SR030PC30" - -/* - * Register offsets within a page - * b15..b8 - page id, b7..b0 - register address - */ -#define POWER_CTRL_REG 0x0001 -#define PAGEMODE_REG 0x03 -#define DEVICE_ID_REG 0x0004 -#define NOON010PC30_ID 0x86 -#define SR030PC30_ID 0x8C -#define VDO_CTL1_REG 0x0010 -#define SUBSAMPL_NONE_VGA 0 -#define SUBSAMPL_QVGA 0x10 -#define SUBSAMPL_QQVGA 0x20 -#define VDO_CTL2_REG 0x0011 -#define SYNC_CTL_REG 0x0012 -#define WIN_ROWH_REG 0x0020 -#define WIN_ROWL_REG 0x0021 -#define WIN_COLH_REG 0x0022 -#define WIN_COLL_REG 0x0023 -#define WIN_HEIGHTH_REG 0x0024 -#define WIN_HEIGHTL_REG 0x0025 -#define WIN_WIDTHH_REG 0x0026 -#define WIN_WIDTHL_REG 0x0027 -#define HBLANKH_REG 0x0040 -#define HBLANKL_REG 0x0041 -#define VSYNCH_REG 0x0042 -#define VSYNCL_REG 0x0043 -/* page 10 */ -#define ISP_CTL_REG(n) (0x1010 + (n)) -#define YOFS_REG 0x1040 -#define DARK_YOFS_REG 0x1041 -#define AG_ABRTH_REG 0x1050 -#define SAT_CTL_REG 0x1060 -#define BSAT_REG 0x1061 -#define RSAT_REG 0x1062 -#define AG_SAT_TH_REG 0x1063 -/* page 11 */ -#define ZLPF_CTRL_REG 0x1110 -#define ZLPF_CTRL2_REG 0x1112 -#define ZLPF_AGH_THR_REG 0x1121 -#define ZLPF_THR_REG 0x1160 -#define ZLPF_DYN_THR_REG 0x1160 -/* page 12 */ -#define YCLPF_CTL1_REG 0x1240 -#define YCLPF_CTL2_REG 0x1241 -#define YCLPF_THR_REG 0x1250 -#define BLPF_CTL_REG 0x1270 -#define BLPF_THR1_REG 0x1274 -#define BLPF_THR2_REG 0x1275 -/* page 14 - Lens Shading Compensation */ -#define LENS_CTRL_REG 0x1410 -#define LENS_XCEN_REG 0x1420 -#define LENS_YCEN_REG 0x1421 -#define LENS_R_COMP_REG 0x1422 -#define LENS_G_COMP_REG 0x1423 -#define LENS_B_COMP_REG 0x1424 -/* page 15 - Color correction */ -#define CMC_CTL_REG 0x1510 -#define CMC_OFSGH_REG 0x1514 -#define CMC_OFSGL_REG 0x1516 -#define CMC_SIGN_REG 0x1517 -/* Color correction coefficients */ -#define CMC_COEF_REG(n) (0x1530 + (n)) -/* Color correction offset coefficients */ -#define CMC_OFS_REG(n) (0x1540 + (n)) -/* page 16 - Gamma correction */ -#define GMA_CTL_REG 0x1610 -/* Gamma correction coefficients 0.14 */ -#define GMA_COEF_REG(n) (0x1630 + (n)) -/* page 20 - Auto Exposure */ -#define AE_CTL1_REG 0x2010 -#define AE_CTL2_REG 0x2011 -#define AE_FRM_CTL_REG 0x2020 -#define AE_FINE_CTL_REG(n) (0x2028 + (n)) -#define EXP_TIMEH_REG 0x2083 -#define EXP_TIMEM_REG 0x2084 -#define EXP_TIMEL_REG 0x2085 -#define EXP_MMINH_REG 0x2086 -#define EXP_MMINL_REG 0x2087 -#define EXP_MMAXH_REG 0x2088 -#define EXP_MMAXM_REG 0x2089 -#define EXP_MMAXL_REG 0x208A -/* page 22 - Auto White Balance */ -#define AWB_CTL1_REG 0x2210 -#define AWB_ENABLE 0x80 -#define AWB_CTL2_REG 0x2211 -#define MWB_ENABLE 0x01 -/* RGB gain control (manual WB) when AWB_CTL1[7]=0 */ -#define AWB_RGAIN_REG 0x2280 -#define AWB_GGAIN_REG 0x2281 -#define AWB_BGAIN_REG 0x2282 -#define AWB_RMAX_REG 0x2283 -#define AWB_RMIN_REG 0x2284 -#define AWB_BMAX_REG 0x2285 -#define AWB_BMIN_REG 0x2286 -/* R, B gain range in bright light conditions */ -#define AWB_RMAXB_REG 0x2287 -#define AWB_RMINB_REG 0x2288 -#define AWB_BMAXB_REG 0x2289 -#define AWB_BMINB_REG 0x228A -/* manual white balance, when AWB_CTL2[0]=1 */ -#define MWB_RGAIN_REG 0x22B2 -#define MWB_BGAIN_REG 0x22B3 -/* the token to mark an array end */ -#define REG_TERM 0xFFFF - -/* Minimum and maximum exposure time in ms */ -#define EXPOS_MIN_MS 1 -#define EXPOS_MAX_MS 125 - -struct sr030pc30_info { - struct v4l2_subdev sd; - const struct sr030pc30_platform_data *pdata; - const struct sr030pc30_format *curr_fmt; - const struct sr030pc30_frmsize *curr_win; - unsigned int auto_wb:1; - unsigned int auto_exp:1; - unsigned int hflip:1; - unsigned int vflip:1; - unsigned int sleep:1; - unsigned int exposure; - u8 blue_balance; - u8 red_balance; - u8 i2c_reg_page; -}; - -struct sr030pc30_format { - enum v4l2_mbus_pixelcode code; - enum v4l2_colorspace colorspace; - u16 ispctl1_reg; -}; - -struct sr030pc30_frmsize { - u16 width; - u16 height; - int vid_ctl1; -}; - -struct i2c_regval { - u16 addr; - u16 val; -}; - -static const struct v4l2_queryctrl sr030pc30_ctrl[] = { - { - .id = V4L2_CID_AUTO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto White Balance", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - }, { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Red Balance", - .minimum = 0, - .maximum = 127, - .step = 1, - .default_value = 64, - .flags = 0, - }, { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Blue Balance", - .minimum = 0, - .maximum = 127, - .step = 1, - .default_value = 64, - }, { - .id = V4L2_CID_EXPOSURE_AUTO, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Auto Exposure", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - }, { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = EXPOS_MIN_MS, - .maximum = EXPOS_MAX_MS, - .step = 1, - .default_value = 1, - }, { - } -}; - -/* supported resolutions */ -static const struct sr030pc30_frmsize sr030pc30_sizes[] = { - { - .width = 640, - .height = 480, - .vid_ctl1 = SUBSAMPL_NONE_VGA, - }, { - .width = 320, - .height = 240, - .vid_ctl1 = SUBSAMPL_QVGA, - }, { - .width = 160, - .height = 120, - .vid_ctl1 = SUBSAMPL_QQVGA, - }, -}; - -/* supported pixel formats */ -static const struct sr030pc30_format sr030pc30_formats[] = { - { - .code = V4L2_MBUS_FMT_YUYV8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0x03, - }, { - .code = V4L2_MBUS_FMT_YVYU8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0x02, - }, { - .code = V4L2_MBUS_FMT_VYUY8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0, - }, { - .code = V4L2_MBUS_FMT_UYVY8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0x01, - }, { - .code = V4L2_MBUS_FMT_RGB565_2X8_BE, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0x40, - }, -}; - -static const struct i2c_regval sr030pc30_base_regs[] = { - /* Window size and position within pixel matrix */ - { WIN_ROWH_REG, 0x00 }, { WIN_ROWL_REG, 0x06 }, - { WIN_COLH_REG, 0x00 }, { WIN_COLL_REG, 0x06 }, - { WIN_HEIGHTH_REG, 0x01 }, { WIN_HEIGHTL_REG, 0xE0 }, - { WIN_WIDTHH_REG, 0x02 }, { WIN_WIDTHL_REG, 0x80 }, - { HBLANKH_REG, 0x01 }, { HBLANKL_REG, 0x50 }, - { VSYNCH_REG, 0x00 }, { VSYNCL_REG, 0x14 }, - { SYNC_CTL_REG, 0 }, - /* Color corection and saturation */ - { ISP_CTL_REG(0), 0x30 }, { YOFS_REG, 0x80 }, - { DARK_YOFS_REG, 0x04 }, { AG_ABRTH_REG, 0x78 }, - { SAT_CTL_REG, 0x1F }, { BSAT_REG, 0x90 }, - { AG_SAT_TH_REG, 0xF0 }, { 0x1064, 0x80 }, - { CMC_CTL_REG, 0x03 }, { CMC_OFSGH_REG, 0x3C }, - { CMC_OFSGL_REG, 0x2C }, { CMC_SIGN_REG, 0x2F }, - { CMC_COEF_REG(0), 0xCB }, { CMC_OFS_REG(0), 0x87 }, - { CMC_COEF_REG(1), 0x61 }, { CMC_OFS_REG(1), 0x18 }, - { CMC_COEF_REG(2), 0x16 }, { CMC_OFS_REG(2), 0x91 }, - { CMC_COEF_REG(3), 0x23 }, { CMC_OFS_REG(3), 0x94 }, - { CMC_COEF_REG(4), 0xCE }, { CMC_OFS_REG(4), 0x9f }, - { CMC_COEF_REG(5), 0x2B }, { CMC_OFS_REG(5), 0x33 }, - { CMC_COEF_REG(6), 0x01 }, { CMC_OFS_REG(6), 0x00 }, - { CMC_COEF_REG(7), 0x34 }, { CMC_OFS_REG(7), 0x94 }, - { CMC_COEF_REG(8), 0x75 }, { CMC_OFS_REG(8), 0x14 }, - /* Color corection coefficients */ - { GMA_CTL_REG, 0x03 }, { GMA_COEF_REG(0), 0x00 }, - { GMA_COEF_REG(1), 0x19 }, { GMA_COEF_REG(2), 0x26 }, - { GMA_COEF_REG(3), 0x3B }, { GMA_COEF_REG(4), 0x5D }, - { GMA_COEF_REG(5), 0x79 }, { GMA_COEF_REG(6), 0x8E }, - { GMA_COEF_REG(7), 0x9F }, { GMA_COEF_REG(8), 0xAF }, - { GMA_COEF_REG(9), 0xBD }, { GMA_COEF_REG(10), 0xCA }, - { GMA_COEF_REG(11), 0xDD }, { GMA_COEF_REG(12), 0xEC }, - { GMA_COEF_REG(13), 0xF7 }, { GMA_COEF_REG(14), 0xFF }, - /* Noise reduction, Z-LPF, YC-LPF and BLPF filters setup */ - { ZLPF_CTRL_REG, 0x99 }, { ZLPF_CTRL2_REG, 0x0E }, - { ZLPF_AGH_THR_REG, 0x29 }, { ZLPF_THR_REG, 0x0F }, - { ZLPF_DYN_THR_REG, 0x63 }, { YCLPF_CTL1_REG, 0x23 }, - { YCLPF_CTL2_REG, 0x3B }, { YCLPF_THR_REG, 0x05 }, - { BLPF_CTL_REG, 0x1D }, { BLPF_THR1_REG, 0x05 }, - { BLPF_THR2_REG, 0x04 }, - /* Automatic white balance */ - { AWB_CTL1_REG, 0xFB }, { AWB_CTL2_REG, 0x26 }, - { AWB_RMAX_REG, 0x54 }, { AWB_RMIN_REG, 0x2B }, - { AWB_BMAX_REG, 0x57 }, { AWB_BMIN_REG, 0x29 }, - { AWB_RMAXB_REG, 0x50 }, { AWB_RMINB_REG, 0x43 }, - { AWB_BMAXB_REG, 0x30 }, { AWB_BMINB_REG, 0x22 }, - /* Auto exposure */ - { AE_CTL1_REG, 0x8C }, { AE_CTL2_REG, 0x04 }, - { AE_FRM_CTL_REG, 0x01 }, { AE_FINE_CTL_REG(0), 0x3F }, - { AE_FINE_CTL_REG(1), 0xA3 }, { AE_FINE_CTL_REG(3), 0x34 }, - /* Lens shading compensation */ - { LENS_CTRL_REG, 0x01 }, { LENS_XCEN_REG, 0x80 }, - { LENS_YCEN_REG, 0x70 }, { LENS_R_COMP_REG, 0x53 }, - { LENS_G_COMP_REG, 0x40 }, { LENS_B_COMP_REG, 0x3e }, - { REG_TERM, 0 }, -}; - -static inline struct sr030pc30_info *to_sr030pc30(struct v4l2_subdev *sd) -{ - return container_of(sd, struct sr030pc30_info, sd); -} - -static inline int set_i2c_page(struct sr030pc30_info *info, - struct i2c_client *client, unsigned int reg) -{ - int ret = 0; - u32 page = reg >> 8 & 0xFF; - - if (info->i2c_reg_page != page && (reg & 0xFF) != 0x03) { - ret = i2c_smbus_write_byte_data(client, PAGEMODE_REG, page); - if (!ret) - info->i2c_reg_page = page; - } - return ret; -} - -static int cam_i2c_read(struct v4l2_subdev *sd, u32 reg_addr) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct sr030pc30_info *info = to_sr030pc30(sd); - - int ret = set_i2c_page(info, client, reg_addr); - if (!ret) - ret = i2c_smbus_read_byte_data(client, reg_addr & 0xFF); - return ret; -} - -static int cam_i2c_write(struct v4l2_subdev *sd, u32 reg_addr, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct sr030pc30_info *info = to_sr030pc30(sd); - - int ret = set_i2c_page(info, client, reg_addr); - if (!ret) - ret = i2c_smbus_write_byte_data( - client, reg_addr & 0xFF, val); - return ret; -} - -static inline int sr030pc30_bulk_write_reg(struct v4l2_subdev *sd, - const struct i2c_regval *msg) -{ - while (msg->addr != REG_TERM) { - int ret = cam_i2c_write(sd, msg->addr, msg->val); - if (ret) - return ret; - msg++; - } - return 0; -} - -/* Device reset and sleep mode control */ -static int sr030pc30_pwr_ctrl(struct v4l2_subdev *sd, - bool reset, bool sleep) -{ - struct sr030pc30_info *info = to_sr030pc30(sd); - u8 reg = sleep ? 0xF1 : 0xF0; - int ret = 0; - - if (reset) - ret = cam_i2c_write(sd, POWER_CTRL_REG, reg | 0x02); - if (!ret) { - ret = cam_i2c_write(sd, POWER_CTRL_REG, reg); - if (!ret) { - info->sleep = sleep; - if (reset) - info->i2c_reg_page = -1; - } - } - return ret; -} - -static inline int sr030pc30_enable_autoexposure(struct v4l2_subdev *sd, int on) -{ - struct sr030pc30_info *info = to_sr030pc30(sd); - /* auto anti-flicker is also enabled here */ - int ret = cam_i2c_write(sd, AE_CTL1_REG, on ? 0xDC : 0x0C); - if (!ret) - info->auto_exp = on; - return ret; -} - -static int sr030pc30_set_exposure(struct v4l2_subdev *sd, int value) -{ - struct sr030pc30_info *info = to_sr030pc30(sd); - - unsigned long expos = value * info->pdata->clk_rate / (8 * 1000); - - int ret = cam_i2c_write(sd, EXP_TIMEH_REG, expos >> 16 & 0xFF); - if (!ret) - ret = cam_i2c_write(sd, EXP_TIMEM_REG, expos >> 8 & 0xFF); - if (!ret) - ret = cam_i2c_write(sd, EXP_TIMEL_REG, expos & 0xFF); - if (!ret) { /* Turn off AE */ - info->exposure = value; - ret = sr030pc30_enable_autoexposure(sd, 0); - } - return ret; -} - -/* Automatic white balance control */ -static int sr030pc30_enable_autowhitebalance(struct v4l2_subdev *sd, int on) -{ - struct sr030pc30_info *info = to_sr030pc30(sd); - - int ret = cam_i2c_write(sd, AWB_CTL2_REG, on ? 0x2E : 0x2F); - if (!ret) - ret = cam_i2c_write(sd, AWB_CTL1_REG, on ? 0xFB : 0x7B); - if (!ret) - info->auto_wb = on; - - return ret; -} - -static int sr030pc30_set_flip(struct v4l2_subdev *sd) -{ - struct sr030pc30_info *info = to_sr030pc30(sd); - - s32 reg = cam_i2c_read(sd, VDO_CTL2_REG); - if (reg < 0) - return reg; - - reg &= 0x7C; - if (info->hflip) - reg |= 0x01; - if (info->vflip) - reg |= 0x02; - return cam_i2c_write(sd, VDO_CTL2_REG, reg | 0x80); -} - -/* Configure resolution, color format and image flip */ -static int sr030pc30_set_params(struct v4l2_subdev *sd) -{ - struct sr030pc30_info *info = to_sr030pc30(sd); - int ret; - - if (!info->curr_win) - return -EINVAL; - - /* Configure the resolution through subsampling */ - ret = cam_i2c_write(sd, VDO_CTL1_REG, - info->curr_win->vid_ctl1); - - if (!ret && info->curr_fmt) - ret = cam_i2c_write(sd, ISP_CTL_REG(0), - info->curr_fmt->ispctl1_reg); - if (!ret) - ret = sr030pc30_set_flip(sd); - - return ret; -} - -/* Find nearest matching image pixel size. */ -static int sr030pc30_try_frame_size(struct v4l2_mbus_framefmt *mf) -{ - unsigned int min_err = ~0; - int i = ARRAY_SIZE(sr030pc30_sizes); - const struct sr030pc30_frmsize *fsize = &sr030pc30_sizes[0], - *match = NULL; - while (i--) { - int err = abs(fsize->width - mf->width) - + abs(fsize->height - mf->height); - if (err < min_err) { - min_err = err; - match = fsize; - } - fsize++; - } - if (match) { - mf->width = match->width; - mf->height = match->height; - return 0; - } - return -EINVAL; -} - -static int sr030pc30_queryctrl(struct v4l2_subdev *sd, - struct v4l2_queryctrl *qc) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(sr030pc30_ctrl); i++) - if (qc->id == sr030pc30_ctrl[i].id) { - *qc = sr030pc30_ctrl[i]; - v4l2_dbg(1, debug, sd, "%s id: %d\n", - __func__, qc->id); - return 0; - } - - return -EINVAL; -} - -static inline int sr030pc30_set_bluebalance(struct v4l2_subdev *sd, int value) -{ - int ret = cam_i2c_write(sd, MWB_BGAIN_REG, value); - if (!ret) - to_sr030pc30(sd)->blue_balance = value; - return ret; -} - -static inline int sr030pc30_set_redbalance(struct v4l2_subdev *sd, int value) -{ - int ret = cam_i2c_write(sd, MWB_RGAIN_REG, value); - if (!ret) - to_sr030pc30(sd)->red_balance = value; - return ret; -} - -static int sr030pc30_s_ctrl(struct v4l2_subdev *sd, - struct v4l2_control *ctrl) -{ - int i, ret = 0; - - for (i = 0; i < ARRAY_SIZE(sr030pc30_ctrl); i++) - if (ctrl->id == sr030pc30_ctrl[i].id) - break; - - if (i == ARRAY_SIZE(sr030pc30_ctrl)) - return -EINVAL; - - if (ctrl->value < sr030pc30_ctrl[i].minimum || - ctrl->value > sr030pc30_ctrl[i].maximum) - return -ERANGE; - - v4l2_dbg(1, debug, sd, "%s: ctrl_id: %d, value: %d\n", - __func__, ctrl->id, ctrl->value); - - switch (ctrl->id) { - case V4L2_CID_AUTO_WHITE_BALANCE: - sr030pc30_enable_autowhitebalance(sd, ctrl->value); - break; - case V4L2_CID_BLUE_BALANCE: - ret = sr030pc30_set_bluebalance(sd, ctrl->value); - break; - case V4L2_CID_RED_BALANCE: - ret = sr030pc30_set_redbalance(sd, ctrl->value); - break; - case V4L2_CID_EXPOSURE_AUTO: - sr030pc30_enable_autoexposure(sd, - ctrl->value == V4L2_EXPOSURE_AUTO); - break; - case V4L2_CID_EXPOSURE: - ret = sr030pc30_set_exposure(sd, ctrl->value); - break; - default: - return -EINVAL; - } - - return ret; -} - -static int sr030pc30_g_ctrl(struct v4l2_subdev *sd, - struct v4l2_control *ctrl) -{ - struct sr030pc30_info *info = to_sr030pc30(sd); - - v4l2_dbg(1, debug, sd, "%s: id: %d\n", __func__, ctrl->id); - - switch (ctrl->id) { - case V4L2_CID_AUTO_WHITE_BALANCE: - ctrl->value = info->auto_wb; - break; - case V4L2_CID_BLUE_BALANCE: - ctrl->value = info->blue_balance; - break; - case V4L2_CID_RED_BALANCE: - ctrl->value = info->red_balance; - break; - case V4L2_CID_EXPOSURE_AUTO: - ctrl->value = info->auto_exp; - break; - case V4L2_CID_EXPOSURE: - ctrl->value = info->exposure; - break; - default: - return -EINVAL; - } - return 0; -} - -static int sr030pc30_enum_fmt(struct v4l2_subdev *sd, unsigned int index, - enum v4l2_mbus_pixelcode *code) -{ - if (!code || index >= ARRAY_SIZE(sr030pc30_formats)) - return -EINVAL; - - *code = sr030pc30_formats[index].code; - return 0; -} - -static int sr030pc30_g_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - struct sr030pc30_info *info = to_sr030pc30(sd); - int ret; - - if (!mf) - return -EINVAL; - - if (!info->curr_win || !info->curr_fmt) { - ret = sr030pc30_set_params(sd); - if (ret) - return ret; - } - - mf->width = info->curr_win->width; - mf->height = info->curr_win->height; - mf->code = info->curr_fmt->code; - mf->colorspace = info->curr_fmt->colorspace; - mf->field = V4L2_FIELD_NONE; - - return 0; -} - -/* Return nearest media bus frame format. */ -static const struct sr030pc30_format *try_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - int i = ARRAY_SIZE(sr030pc30_formats); - - sr030pc30_try_frame_size(mf); - - while (i--) - if (mf->code == sr030pc30_formats[i].code) - break; - - mf->code = sr030pc30_formats[i].code; - - return &sr030pc30_formats[i]; -} - -/* Return nearest media bus frame format. */ -static int sr030pc30_try_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - if (!sd || !mf) - return -EINVAL; - - try_fmt(sd, mf); - return 0; -} - -static int sr030pc30_s_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - struct sr030pc30_info *info = to_sr030pc30(sd); - - if (!sd || !mf) - return -EINVAL; - - info->curr_fmt = try_fmt(sd, mf); - - return sr030pc30_set_params(sd); -} - -static int sr030pc30_base_config(struct v4l2_subdev *sd) -{ - struct sr030pc30_info *info = to_sr030pc30(sd); - int ret; - unsigned long expmin, expmax; - - ret = sr030pc30_bulk_write_reg(sd, sr030pc30_base_regs); - if (!ret) { - info->curr_fmt = &sr030pc30_formats[0]; - info->curr_win = &sr030pc30_sizes[0]; - ret = sr030pc30_set_params(sd); - } - if (!ret) - ret = sr030pc30_pwr_ctrl(sd, false, false); - - if (!ret && !info->pdata) - return ret; - - expmin = EXPOS_MIN_MS * info->pdata->clk_rate / (8 * 1000); - expmax = EXPOS_MAX_MS * info->pdata->clk_rate / (8 * 1000); - - v4l2_dbg(1, debug, sd, "%s: expmin= %lx, expmax= %lx", __func__, - expmin, expmax); - - /* Setting up manual exposure time range */ - ret = cam_i2c_write(sd, EXP_MMINH_REG, expmin >> 8 & 0xFF); - if (!ret) - ret = cam_i2c_write(sd, EXP_MMINL_REG, expmin & 0xFF); - if (!ret) - ret = cam_i2c_write(sd, EXP_MMAXH_REG, expmax >> 16 & 0xFF); - if (!ret) - ret = cam_i2c_write(sd, EXP_MMAXM_REG, expmax >> 8 & 0xFF); - if (!ret) - ret = cam_i2c_write(sd, EXP_MMAXL_REG, expmax & 0xFF); - - return ret; -} - -static int sr030pc30_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct sr030pc30_info *info = to_sr030pc30(sd); - const struct sr030pc30_platform_data *pdata = info->pdata; - int ret; - - if (pdata == NULL) { - WARN(1, "No platform data!\n"); - return -EINVAL; - } - - /* - * Put sensor into power sleep mode before switching off - * power and disabling MCLK. - */ - if (!on) - sr030pc30_pwr_ctrl(sd, false, true); - - /* set_power controls sensor's power and clock */ - if (pdata->set_power) { - ret = pdata->set_power(&client->dev, on); - if (ret) - return ret; - } - - if (on) { - ret = sr030pc30_base_config(sd); - } else { - ret = 0; - info->curr_win = NULL; - info->curr_fmt = NULL; - } - - return ret; -} - -static const struct v4l2_subdev_core_ops sr030pc30_core_ops = { - .s_power = sr030pc30_s_power, - .queryctrl = sr030pc30_queryctrl, - .s_ctrl = sr030pc30_s_ctrl, - .g_ctrl = sr030pc30_g_ctrl, -}; - -static const struct v4l2_subdev_video_ops sr030pc30_video_ops = { - .g_mbus_fmt = sr030pc30_g_fmt, - .s_mbus_fmt = sr030pc30_s_fmt, - .try_mbus_fmt = sr030pc30_try_fmt, - .enum_mbus_fmt = sr030pc30_enum_fmt, -}; - -static const struct v4l2_subdev_ops sr030pc30_ops = { - .core = &sr030pc30_core_ops, - .video = &sr030pc30_video_ops, -}; - -/* - * Detect sensor type. Return 0 if SR030PC30 was detected - * or -ENODEV otherwise. - */ -static int sr030pc30_detect(struct i2c_client *client) -{ - const struct sr030pc30_platform_data *pdata - = client->dev.platform_data; - int ret; - - /* Enable sensor's power and clock */ - if (pdata->set_power) { - ret = pdata->set_power(&client->dev, 1); - if (ret) - return ret; - } - - ret = i2c_smbus_read_byte_data(client, DEVICE_ID_REG); - - if (pdata->set_power) - pdata->set_power(&client->dev, 0); - - if (ret < 0) { - dev_err(&client->dev, "%s: I2C read failed\n", __func__); - return ret; - } - - return ret == SR030PC30_ID ? 0 : -ENODEV; -} - - -static int sr030pc30_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct sr030pc30_info *info; - struct v4l2_subdev *sd; - const struct sr030pc30_platform_data *pdata - = client->dev.platform_data; - int ret; - - if (!pdata) { - dev_err(&client->dev, "No platform data!"); - return -EIO; - } - - ret = sr030pc30_detect(client); - if (ret) - return ret; - - info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - sd = &info->sd; - strcpy(sd->name, MODULE_NAME); - info->pdata = client->dev.platform_data; - - v4l2_i2c_subdev_init(sd, client, &sr030pc30_ops); - - info->i2c_reg_page = -1; - info->hflip = 1; - info->auto_exp = 1; - info->exposure = 30; - - return 0; -} - -static int sr030pc30_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct sr030pc30_info *info = to_sr030pc30(sd); - - v4l2_device_unregister_subdev(sd); - kfree(info); - return 0; -} - -static const struct i2c_device_id sr030pc30_id[] = { - { MODULE_NAME, 0 }, - { }, -}; -MODULE_DEVICE_TABLE(i2c, sr030pc30_id); - - -static struct i2c_driver sr030pc30_i2c_driver = { - .driver = { - .name = MODULE_NAME - }, - .probe = sr030pc30_probe, - .remove = sr030pc30_remove, - .id_table = sr030pc30_id, -}; - -module_i2c_driver(sr030pc30_i2c_driver); - -MODULE_DESCRIPTION("Siliconfile SR030PC30 camera driver"); -MODULE_AUTHOR("Sylwester Nawrocki "); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/tcm825x.c b/drivers/media/video/tcm825x.c deleted file mode 100644 index 462caa44ae0..00000000000 --- a/drivers/media/video/tcm825x.c +++ /dev/null @@ -1,937 +0,0 @@ -/* - * drivers/media/video/tcm825x.c - * - * TCM825X camera sensor driver. - * - * Copyright (C) 2007 Nokia Corporation. - * - * Contact: Sakari Ailus - * - * Based on code from David Cohen - * - * This driver was based on ov9640 sensor driver from MontaVista - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#include -#include -#include - -#include "tcm825x.h" - -/* - * The sensor has two fps modes: the lower one just gives half the fps - * at the same xclk than the high one. - */ -#define MAX_FPS 30 -#define MIN_FPS 8 -#define MAX_HALF_FPS (MAX_FPS / 2) -#define HIGH_FPS_MODE_LOWER_LIMIT 14 -#define DEFAULT_FPS MAX_HALF_FPS - -struct tcm825x_sensor { - const struct tcm825x_platform_data *platform_data; - struct v4l2_int_device *v4l2_int_device; - struct i2c_client *i2c_client; - struct v4l2_pix_format pix; - struct v4l2_fract timeperframe; -}; - -/* list of image formats supported by TCM825X sensor */ -static const struct v4l2_fmtdesc tcm825x_formats[] = { - { - .description = "YUYV (YUV 4:2:2), packed", - .pixelformat = V4L2_PIX_FMT_UYVY, - }, { - /* Note: V4L2 defines RGB565 as: - * - * Byte 0 Byte 1 - * g2 g1 g0 r4 r3 r2 r1 r0 b4 b3 b2 b1 b0 g5 g4 g3 - * - * We interpret RGB565 as: - * - * Byte 0 Byte 1 - * g2 g1 g0 b4 b3 b2 b1 b0 r4 r3 r2 r1 r0 g5 g4 g3 - */ - .description = "RGB565, le", - .pixelformat = V4L2_PIX_FMT_RGB565, - }, -}; - -#define TCM825X_NUM_CAPTURE_FORMATS ARRAY_SIZE(tcm825x_formats) - -/* - * TCM825X register configuration for all combinations of pixel format and - * image size - */ -static const struct tcm825x_reg subqcif = { 0x20, TCM825X_PICSIZ }; -static const struct tcm825x_reg qcif = { 0x18, TCM825X_PICSIZ }; -static const struct tcm825x_reg cif = { 0x14, TCM825X_PICSIZ }; -static const struct tcm825x_reg qqvga = { 0x0c, TCM825X_PICSIZ }; -static const struct tcm825x_reg qvga = { 0x04, TCM825X_PICSIZ }; -static const struct tcm825x_reg vga = { 0x00, TCM825X_PICSIZ }; - -static const struct tcm825x_reg yuv422 = { 0x00, TCM825X_PICFMT }; -static const struct tcm825x_reg rgb565 = { 0x02, TCM825X_PICFMT }; - -/* Our own specific controls */ -#define V4L2_CID_ALC V4L2_CID_PRIVATE_BASE -#define V4L2_CID_H_EDGE_EN V4L2_CID_PRIVATE_BASE + 1 -#define V4L2_CID_V_EDGE_EN V4L2_CID_PRIVATE_BASE + 2 -#define V4L2_CID_LENS V4L2_CID_PRIVATE_BASE + 3 -#define V4L2_CID_MAX_EXPOSURE_TIME V4L2_CID_PRIVATE_BASE + 4 -#define V4L2_CID_LAST_PRIV V4L2_CID_MAX_EXPOSURE_TIME - -/* Video controls */ -static struct vcontrol { - struct v4l2_queryctrl qc; - u16 reg; - u16 start_bit; -} video_control[] = { - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 0, - .maximum = 63, - .step = 1, - }, - .reg = TCM825X_AG, - .start_bit = 0, - }, - { - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Red Balance", - .minimum = 0, - .maximum = 255, - .step = 1, - }, - .reg = TCM825X_MRG, - .start_bit = 0, - }, - { - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Blue Balance", - .minimum = 0, - .maximum = 255, - .step = 1, - }, - .reg = TCM825X_MBG, - .start_bit = 0, - }, - { - { - .id = V4L2_CID_AUTO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto White Balance", - .minimum = 0, - .maximum = 1, - .step = 0, - }, - .reg = TCM825X_AWBSW, - .start_bit = 7, - }, - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure Time", - .minimum = 0, - .maximum = 0x1fff, - .step = 1, - }, - .reg = TCM825X_ESRSPD_U, - .start_bit = 0, - }, - { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Mirror Image", - .minimum = 0, - .maximum = 1, - .step = 0, - }, - .reg = TCM825X_H_INV, - .start_bit = 6, - }, - { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Vertical Flip", - .minimum = 0, - .maximum = 1, - .step = 0, - }, - .reg = TCM825X_V_INV, - .start_bit = 7, - }, - /* Private controls */ - { - { - .id = V4L2_CID_ALC, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto Luminance Control", - .minimum = 0, - .maximum = 1, - .step = 0, - }, - .reg = TCM825X_ALCSW, - .start_bit = 7, - }, - { - { - .id = V4L2_CID_H_EDGE_EN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Horizontal Edge Enhancement", - .minimum = 0, - .maximum = 0xff, - .step = 1, - }, - .reg = TCM825X_HDTG, - .start_bit = 0, - }, - { - { - .id = V4L2_CID_V_EDGE_EN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Vertical Edge Enhancement", - .minimum = 0, - .maximum = 0xff, - .step = 1, - }, - .reg = TCM825X_VDTG, - .start_bit = 0, - }, - { - { - .id = V4L2_CID_LENS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Lens Shading Compensation", - .minimum = 0, - .maximum = 0x3f, - .step = 1, - }, - .reg = TCM825X_LENS, - .start_bit = 0, - }, - { - { - .id = V4L2_CID_MAX_EXPOSURE_TIME, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Maximum Exposure Time", - .minimum = 0, - .maximum = 0x3, - .step = 1, - }, - .reg = TCM825X_ESRLIM, - .start_bit = 5, - }, -}; - - -static const struct tcm825x_reg *tcm825x_siz_reg[NUM_IMAGE_SIZES] = -{ &subqcif, &qqvga, &qcif, &qvga, &cif, &vga }; - -static const struct tcm825x_reg *tcm825x_fmt_reg[NUM_PIXEL_FORMATS] = -{ &yuv422, &rgb565 }; - -/* - * Read a value from a register in an TCM825X sensor device. The value is - * returned in 'val'. - * Returns zero if successful, or non-zero otherwise. - */ -static int tcm825x_read_reg(struct i2c_client *client, int reg) -{ - int err; - struct i2c_msg msg[2]; - u8 reg_buf, data_buf = 0; - - if (!client->adapter) - return -ENODEV; - - msg[0].addr = client->addr; - msg[0].flags = 0; - msg[0].len = 1; - msg[0].buf = ®_buf; - msg[1].addr = client->addr; - msg[1].flags = I2C_M_RD; - msg[1].len = 1; - msg[1].buf = &data_buf; - - reg_buf = reg; - - err = i2c_transfer(client->adapter, msg, 2); - if (err < 0) - return err; - return data_buf; -} - -/* - * Write a value to a register in an TCM825X sensor device. - * Returns zero if successful, or non-zero otherwise. - */ -static int tcm825x_write_reg(struct i2c_client *client, u8 reg, u8 val) -{ - int err; - struct i2c_msg msg[1]; - unsigned char data[2]; - - if (!client->adapter) - return -ENODEV; - - msg->addr = client->addr; - msg->flags = 0; - msg->len = 2; - msg->buf = data; - data[0] = reg; - data[1] = val; - err = i2c_transfer(client->adapter, msg, 1); - if (err >= 0) - return 0; - return err; -} - -static int __tcm825x_write_reg_mask(struct i2c_client *client, - u8 reg, u8 val, u8 mask) -{ - int rc; - - /* need to do read - modify - write */ - rc = tcm825x_read_reg(client, reg); - if (rc < 0) - return rc; - - rc &= (~mask); /* Clear the masked bits */ - val &= mask; /* Enforce mask on value */ - val |= rc; - - /* write the new value to the register */ - rc = tcm825x_write_reg(client, reg, val); - if (rc) - return rc; - - return 0; -} - -#define tcm825x_write_reg_mask(client, regmask, val) \ - __tcm825x_write_reg_mask(client, TCM825X_ADDR((regmask)), val, \ - TCM825X_MASK((regmask))) - - -/* - * Initialize a list of TCM825X registers. - * The list of registers is terminated by the pair of values - * { TCM825X_REG_TERM, TCM825X_VAL_TERM }. - * Returns zero if successful, or non-zero otherwise. - */ -static int tcm825x_write_default_regs(struct i2c_client *client, - const struct tcm825x_reg *reglist) -{ - int err; - const struct tcm825x_reg *next = reglist; - - while (!((next->reg == TCM825X_REG_TERM) - && (next->val == TCM825X_VAL_TERM))) { - err = tcm825x_write_reg(client, next->reg, next->val); - if (err) { - dev_err(&client->dev, "register writing failed\n"); - return err; - } - next++; - } - - return 0; -} - -static struct vcontrol *find_vctrl(int id) -{ - int i; - - if (id < V4L2_CID_BASE) - return NULL; - - for (i = 0; i < ARRAY_SIZE(video_control); i++) - if (video_control[i].qc.id == id) - return &video_control[i]; - - return NULL; -} - -/* - * Find the best match for a requested image capture size. The best match - * is chosen as the nearest match that has the same number or fewer pixels - * as the requested size, or the smallest image size if the requested size - * has fewer pixels than the smallest image. - */ -static enum image_size tcm825x_find_size(struct v4l2_int_device *s, - unsigned int width, - unsigned int height) -{ - enum image_size isize; - unsigned long pixels = width * height; - struct tcm825x_sensor *sensor = s->priv; - - for (isize = subQCIF; isize < VGA; isize++) { - if (tcm825x_sizes[isize + 1].height - * tcm825x_sizes[isize + 1].width > pixels) { - dev_dbg(&sensor->i2c_client->dev, "size %d\n", isize); - - return isize; - } - } - - dev_dbg(&sensor->i2c_client->dev, "format default VGA\n"); - - return VGA; -} - -/* - * Configure the TCM825X for current image size, pixel format, and - * frame period. fper is the frame period (in seconds) expressed as a - * fraction. Returns zero if successful, or non-zero otherwise. The - * actual frame period is returned in fper. - */ -static int tcm825x_configure(struct v4l2_int_device *s) -{ - struct tcm825x_sensor *sensor = s->priv; - struct v4l2_pix_format *pix = &sensor->pix; - enum image_size isize = tcm825x_find_size(s, pix->width, pix->height); - struct v4l2_fract *fper = &sensor->timeperframe; - enum pixel_format pfmt; - int err; - u32 tgt_fps; - u8 val; - - /* common register initialization */ - err = tcm825x_write_default_regs( - sensor->i2c_client, sensor->platform_data->default_regs()); - if (err) - return err; - - /* configure image size */ - val = tcm825x_siz_reg[isize]->val; - dev_dbg(&sensor->i2c_client->dev, - "configuring image size %d\n", isize); - err = tcm825x_write_reg_mask(sensor->i2c_client, - tcm825x_siz_reg[isize]->reg, val); - if (err) - return err; - - /* configure pixel format */ - switch (pix->pixelformat) { - default: - case V4L2_PIX_FMT_RGB565: - pfmt = RGB565; - break; - case V4L2_PIX_FMT_UYVY: - pfmt = YUV422; - break; - } - - dev_dbg(&sensor->i2c_client->dev, - "configuring pixel format %d\n", pfmt); - val = tcm825x_fmt_reg[pfmt]->val; - - err = tcm825x_write_reg_mask(sensor->i2c_client, - tcm825x_fmt_reg[pfmt]->reg, val); - if (err) - return err; - - /* - * For frame rate < 15, the FPS reg (addr 0x02, bit 7) must be - * set. Frame rate will be halved from the normal. - */ - tgt_fps = fper->denominator / fper->numerator; - if (tgt_fps <= HIGH_FPS_MODE_LOWER_LIMIT) { - val = tcm825x_read_reg(sensor->i2c_client, 0x02); - val |= 0x80; - tcm825x_write_reg(sensor->i2c_client, 0x02, val); - } - - return 0; -} - -static int ioctl_queryctrl(struct v4l2_int_device *s, - struct v4l2_queryctrl *qc) -{ - struct vcontrol *control; - - control = find_vctrl(qc->id); - - if (control == NULL) - return -EINVAL; - - *qc = control->qc; - - return 0; -} - -static int ioctl_g_ctrl(struct v4l2_int_device *s, - struct v4l2_control *vc) -{ - struct tcm825x_sensor *sensor = s->priv; - struct i2c_client *client = sensor->i2c_client; - int val, r; - struct vcontrol *lvc; - - /* exposure time is special, spread across 2 registers */ - if (vc->id == V4L2_CID_EXPOSURE) { - int val_lower, val_upper; - - val_upper = tcm825x_read_reg(client, - TCM825X_ADDR(TCM825X_ESRSPD_U)); - if (val_upper < 0) - return val_upper; - val_lower = tcm825x_read_reg(client, - TCM825X_ADDR(TCM825X_ESRSPD_L)); - if (val_lower < 0) - return val_lower; - - vc->value = ((val_upper & 0x1f) << 8) | (val_lower); - return 0; - } - - lvc = find_vctrl(vc->id); - if (lvc == NULL) - return -EINVAL; - - r = tcm825x_read_reg(client, TCM825X_ADDR(lvc->reg)); - if (r < 0) - return r; - val = r & TCM825X_MASK(lvc->reg); - val >>= lvc->start_bit; - - if (val < 0) - return val; - - if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP) - val ^= sensor->platform_data->is_upside_down(); - - vc->value = val; - return 0; -} - -static int ioctl_s_ctrl(struct v4l2_int_device *s, - struct v4l2_control *vc) -{ - struct tcm825x_sensor *sensor = s->priv; - struct i2c_client *client = sensor->i2c_client; - struct vcontrol *lvc; - int val = vc->value; - - /* exposure time is special, spread across 2 registers */ - if (vc->id == V4L2_CID_EXPOSURE) { - int val_lower, val_upper; - val_lower = val & TCM825X_MASK(TCM825X_ESRSPD_L); - val_upper = (val >> 8) & TCM825X_MASK(TCM825X_ESRSPD_U); - - if (tcm825x_write_reg_mask(client, - TCM825X_ESRSPD_U, val_upper)) - return -EIO; - - if (tcm825x_write_reg_mask(client, - TCM825X_ESRSPD_L, val_lower)) - return -EIO; - - return 0; - } - - lvc = find_vctrl(vc->id); - if (lvc == NULL) - return -EINVAL; - - if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP) - val ^= sensor->platform_data->is_upside_down(); - - val = val << lvc->start_bit; - if (tcm825x_write_reg_mask(client, lvc->reg, val)) - return -EIO; - - return 0; -} - -static int ioctl_enum_fmt_cap(struct v4l2_int_device *s, - struct v4l2_fmtdesc *fmt) -{ - int index = fmt->index; - - switch (fmt->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (index >= TCM825X_NUM_CAPTURE_FORMATS) - return -EINVAL; - break; - - default: - return -EINVAL; - } - - fmt->flags = tcm825x_formats[index].flags; - strlcpy(fmt->description, tcm825x_formats[index].description, - sizeof(fmt->description)); - fmt->pixelformat = tcm825x_formats[index].pixelformat; - - return 0; -} - -static int ioctl_try_fmt_cap(struct v4l2_int_device *s, - struct v4l2_format *f) -{ - struct tcm825x_sensor *sensor = s->priv; - enum image_size isize; - int ifmt; - struct v4l2_pix_format *pix = &f->fmt.pix; - - isize = tcm825x_find_size(s, pix->width, pix->height); - dev_dbg(&sensor->i2c_client->dev, "isize = %d num_capture = %lu\n", - isize, (unsigned long)TCM825X_NUM_CAPTURE_FORMATS); - - pix->width = tcm825x_sizes[isize].width; - pix->height = tcm825x_sizes[isize].height; - - for (ifmt = 0; ifmt < TCM825X_NUM_CAPTURE_FORMATS; ifmt++) - if (pix->pixelformat == tcm825x_formats[ifmt].pixelformat) - break; - - if (ifmt == TCM825X_NUM_CAPTURE_FORMATS) - ifmt = 0; /* Default = YUV 4:2:2 */ - - pix->pixelformat = tcm825x_formats[ifmt].pixelformat; - pix->field = V4L2_FIELD_NONE; - pix->bytesperline = pix->width * TCM825X_BYTES_PER_PIXEL; - pix->sizeimage = pix->bytesperline * pix->height; - pix->priv = 0; - dev_dbg(&sensor->i2c_client->dev, "format = 0x%08x\n", - pix->pixelformat); - - switch (pix->pixelformat) { - case V4L2_PIX_FMT_UYVY: - default: - pix->colorspace = V4L2_COLORSPACE_JPEG; - break; - case V4L2_PIX_FMT_RGB565: - pix->colorspace = V4L2_COLORSPACE_SRGB; - break; - } - - return 0; -} - -static int ioctl_s_fmt_cap(struct v4l2_int_device *s, - struct v4l2_format *f) -{ - struct tcm825x_sensor *sensor = s->priv; - struct v4l2_pix_format *pix = &f->fmt.pix; - int rval; - - rval = ioctl_try_fmt_cap(s, f); - if (rval) - return rval; - - rval = tcm825x_configure(s); - - sensor->pix = *pix; - - return rval; -} - -static int ioctl_g_fmt_cap(struct v4l2_int_device *s, - struct v4l2_format *f) -{ - struct tcm825x_sensor *sensor = s->priv; - - f->fmt.pix = sensor->pix; - - return 0; -} - -static int ioctl_g_parm(struct v4l2_int_device *s, - struct v4l2_streamparm *a) -{ - struct tcm825x_sensor *sensor = s->priv; - struct v4l2_captureparm *cparm = &a->parm.capture; - - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - memset(a, 0, sizeof(*a)); - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - cparm->capability = V4L2_CAP_TIMEPERFRAME; - cparm->timeperframe = sensor->timeperframe; - - return 0; -} - -static int ioctl_s_parm(struct v4l2_int_device *s, - struct v4l2_streamparm *a) -{ - struct tcm825x_sensor *sensor = s->priv; - struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe; - u32 tgt_fps; /* target frames per secound */ - int rval; - - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if ((timeperframe->numerator == 0) - || (timeperframe->denominator == 0)) { - timeperframe->denominator = DEFAULT_FPS; - timeperframe->numerator = 1; - } - - tgt_fps = timeperframe->denominator / timeperframe->numerator; - - if (tgt_fps > MAX_FPS) { - timeperframe->denominator = MAX_FPS; - timeperframe->numerator = 1; - } else if (tgt_fps < MIN_FPS) { - timeperframe->denominator = MIN_FPS; - timeperframe->numerator = 1; - } - - sensor->timeperframe = *timeperframe; - - rval = tcm825x_configure(s); - - return rval; -} - -static int ioctl_s_power(struct v4l2_int_device *s, int on) -{ - struct tcm825x_sensor *sensor = s->priv; - - return sensor->platform_data->power_set(on); -} - -/* - * Given the image capture format in pix, the nominal frame period in - * timeperframe, calculate the required xclk frequency. - * - * TCM825X input frequency characteristics are: - * Minimum 11.9 MHz, Typical 24.57 MHz and maximum 25/27 MHz - */ - -static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) -{ - struct tcm825x_sensor *sensor = s->priv; - struct v4l2_fract *timeperframe = &sensor->timeperframe; - u32 tgt_xclk; /* target xclk */ - u32 tgt_fps; /* target frames per secound */ - int rval; - - rval = sensor->platform_data->ifparm(p); - if (rval) - return rval; - - tgt_fps = timeperframe->denominator / timeperframe->numerator; - - tgt_xclk = (tgt_fps <= HIGH_FPS_MODE_LOWER_LIMIT) ? - (2457 * tgt_fps) / MAX_HALF_FPS : - (2457 * tgt_fps) / MAX_FPS; - tgt_xclk *= 10000; - - tgt_xclk = min(tgt_xclk, (u32)TCM825X_XCLK_MAX); - tgt_xclk = max(tgt_xclk, (u32)TCM825X_XCLK_MIN); - - p->u.bt656.clock_curr = tgt_xclk; - - return 0; -} - -static int ioctl_g_needs_reset(struct v4l2_int_device *s, void *buf) -{ - struct tcm825x_sensor *sensor = s->priv; - - return sensor->platform_data->needs_reset(s, buf, &sensor->pix); -} - -static int ioctl_reset(struct v4l2_int_device *s) -{ - return -EBUSY; -} - -static int ioctl_init(struct v4l2_int_device *s) -{ - return tcm825x_configure(s); -} - -static int ioctl_dev_exit(struct v4l2_int_device *s) -{ - return 0; -} - -static int ioctl_dev_init(struct v4l2_int_device *s) -{ - struct tcm825x_sensor *sensor = s->priv; - int r; - - r = tcm825x_read_reg(sensor->i2c_client, 0x01); - if (r < 0) - return r; - if (r == 0) { - dev_err(&sensor->i2c_client->dev, "device not detected\n"); - return -EIO; - } - return 0; -} - -static struct v4l2_int_ioctl_desc tcm825x_ioctl_desc[] = { - { vidioc_int_dev_init_num, - (v4l2_int_ioctl_func *)ioctl_dev_init }, - { vidioc_int_dev_exit_num, - (v4l2_int_ioctl_func *)ioctl_dev_exit }, - { vidioc_int_s_power_num, - (v4l2_int_ioctl_func *)ioctl_s_power }, - { vidioc_int_g_ifparm_num, - (v4l2_int_ioctl_func *)ioctl_g_ifparm }, - { vidioc_int_g_needs_reset_num, - (v4l2_int_ioctl_func *)ioctl_g_needs_reset }, - { vidioc_int_reset_num, - (v4l2_int_ioctl_func *)ioctl_reset }, - { vidioc_int_init_num, - (v4l2_int_ioctl_func *)ioctl_init }, - { vidioc_int_enum_fmt_cap_num, - (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap }, - { vidioc_int_try_fmt_cap_num, - (v4l2_int_ioctl_func *)ioctl_try_fmt_cap }, - { vidioc_int_g_fmt_cap_num, - (v4l2_int_ioctl_func *)ioctl_g_fmt_cap }, - { vidioc_int_s_fmt_cap_num, - (v4l2_int_ioctl_func *)ioctl_s_fmt_cap }, - { vidioc_int_g_parm_num, - (v4l2_int_ioctl_func *)ioctl_g_parm }, - { vidioc_int_s_parm_num, - (v4l2_int_ioctl_func *)ioctl_s_parm }, - { vidioc_int_queryctrl_num, - (v4l2_int_ioctl_func *)ioctl_queryctrl }, - { vidioc_int_g_ctrl_num, - (v4l2_int_ioctl_func *)ioctl_g_ctrl }, - { vidioc_int_s_ctrl_num, - (v4l2_int_ioctl_func *)ioctl_s_ctrl }, -}; - -static struct v4l2_int_slave tcm825x_slave = { - .ioctls = tcm825x_ioctl_desc, - .num_ioctls = ARRAY_SIZE(tcm825x_ioctl_desc), -}; - -static struct tcm825x_sensor tcm825x; - -static struct v4l2_int_device tcm825x_int_device = { - .module = THIS_MODULE, - .name = TCM825X_NAME, - .priv = &tcm825x, - .type = v4l2_int_type_slave, - .u = { - .slave = &tcm825x_slave, - }, -}; - -static int tcm825x_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct tcm825x_sensor *sensor = &tcm825x; - - if (i2c_get_clientdata(client)) - return -EBUSY; - - sensor->platform_data = client->dev.platform_data; - - if (sensor->platform_data == NULL - || !sensor->platform_data->is_okay()) - return -ENODEV; - - sensor->v4l2_int_device = &tcm825x_int_device; - - sensor->i2c_client = client; - i2c_set_clientdata(client, sensor); - - /* Make the default capture format QVGA RGB565 */ - sensor->pix.width = tcm825x_sizes[QVGA].width; - sensor->pix.height = tcm825x_sizes[QVGA].height; - sensor->pix.pixelformat = V4L2_PIX_FMT_RGB565; - - return v4l2_int_device_register(sensor->v4l2_int_device); -} - -static int tcm825x_remove(struct i2c_client *client) -{ - struct tcm825x_sensor *sensor = i2c_get_clientdata(client); - - if (!client->adapter) - return -ENODEV; /* our client isn't attached */ - - v4l2_int_device_unregister(sensor->v4l2_int_device); - - return 0; -} - -static const struct i2c_device_id tcm825x_id[] = { - { "tcm825x", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tcm825x_id); - -static struct i2c_driver tcm825x_i2c_driver = { - .driver = { - .name = TCM825X_NAME, - }, - .probe = tcm825x_probe, - .remove = tcm825x_remove, - .id_table = tcm825x_id, -}; - -static struct tcm825x_sensor tcm825x = { - .timeperframe = { - .numerator = 1, - .denominator = DEFAULT_FPS, - }, -}; - -static int __init tcm825x_init(void) -{ - int rval; - - rval = i2c_add_driver(&tcm825x_i2c_driver); - if (rval) - printk(KERN_INFO "%s: failed registering " TCM825X_NAME "\n", - __func__); - - return rval; -} - -static void __exit tcm825x_exit(void) -{ - i2c_del_driver(&tcm825x_i2c_driver); -} - -/* - * FIXME: Menelaus isn't ready (?) at module_init stage, so use - * late_initcall for now. - */ -late_initcall(tcm825x_init); -module_exit(tcm825x_exit); - -MODULE_AUTHOR("Sakari Ailus "); -MODULE_DESCRIPTION("TCM825x camera sensor driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/tcm825x.h b/drivers/media/video/tcm825x.h deleted file mode 100644 index 5b7e6968236..00000000000 --- a/drivers/media/video/tcm825x.h +++ /dev/null @@ -1,200 +0,0 @@ -/* - * drivers/media/video/tcm825x.h - * - * Register definitions for the TCM825X CameraChip. - * - * Author: David Cohen (david.cohen@indt.org.br) - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - * - * This file was based on ov9640.h from MontaVista - */ - -#ifndef TCM825X_H -#define TCM825X_H - -#include - -#include - -#define TCM825X_NAME "tcm825x" - -#define TCM825X_MASK(x) x & 0x00ff -#define TCM825X_ADDR(x) (x & 0xff00) >> 8 - -/* The TCM825X I2C sensor chip has a fixed slave address of 0x3d. */ -#define TCM825X_I2C_ADDR 0x3d - -/* - * define register offsets for the TCM825X sensor chip - * OFFSET(8 bits) + MASK(8 bits) - * MASK bit 4 and 3 are used when the register uses more than one address - */ -#define TCM825X_FPS 0x0280 -#define TCM825X_ACF 0x0240 -#define TCM825X_DOUTBUF 0x020C -#define TCM825X_DCLKP 0x0202 -#define TCM825X_ACFDET 0x0201 -#define TCM825X_DOUTSW 0x0380 -#define TCM825X_DATAHZ 0x0340 -#define TCM825X_PICSIZ 0x033c -#define TCM825X_PICFMT 0x0302 -#define TCM825X_V_INV 0x0480 -#define TCM825X_H_INV 0x0440 -#define TCM825X_ESRLSW 0x0430 -#define TCM825X_V_LENGTH 0x040F -#define TCM825X_ALCSW 0x0580 -#define TCM825X_ESRLIM 0x0560 -#define TCM825X_ESRSPD_U 0x051F -#define TCM825X_ESRSPD_L 0x06FF -#define TCM825X_AG 0x07FF -#define TCM825X_ESRSPD2 0x06FF -#define TCM825X_ALCMODE 0x0830 -#define TCM825X_ALCH 0x080F -#define TCM825X_ALCL 0x09FF -#define TCM825X_AWBSW 0x0A80 -#define TCM825X_MRG 0x0BFF -#define TCM825X_MBG 0x0CFF -#define TCM825X_GAMSW 0x0D80 -#define TCM825X_HDTG 0x0EFF -#define TCM825X_VDTG 0x0FFF -#define TCM825X_HDTCORE 0x10F0 -#define TCM825X_VDTCORE 0x100F -#define TCM825X_CONT 0x11FF -#define TCM825X_BRIGHT 0x12FF -#define TCM825X_VHUE 0x137F -#define TCM825X_UHUE 0x147F -#define TCM825X_VGAIN 0x153F -#define TCM825X_UGAIN 0x163F -#define TCM825X_UVCORE 0x170F -#define TCM825X_SATU 0x187F -#define TCM825X_MHMODE 0x1980 -#define TCM825X_MHLPFSEL 0x1940 -#define TCM825X_YMODE 0x1930 -#define TCM825X_MIXHG 0x1907 -#define TCM825X_LENS 0x1A3F -#define TCM825X_AGLIM 0x1BE0 -#define TCM825X_LENSRPOL 0x1B10 -#define TCM825X_LENSRGAIN 0x1B0F -#define TCM825X_ES100S 0x1CFF -#define TCM825X_ES120S 0x1DFF -#define TCM825X_DMASK 0x1EC0 -#define TCM825X_CODESW 0x1E20 -#define TCM825X_CODESEL 0x1E10 -#define TCM825X_TESPIC 0x1E04 -#define TCM825X_PICSEL 0x1E03 -#define TCM825X_HNUM 0x20FF -#define TCM825X_VOUTPH 0x287F -#define TCM825X_ESROUT 0x327F -#define TCM825X_ESROUT2 0x33FF -#define TCM825X_AGOUT 0x34FF -#define TCM825X_DGOUT 0x353F -#define TCM825X_AGSLOW1 0x39C0 -#define TCM825X_FLLSMODE 0x3930 -#define TCM825X_FLLSLIM 0x390F -#define TCM825X_DETSEL 0x3AF0 -#define TCM825X_ACDETNC 0x3A0F -#define TCM825X_AGSLOW2 0x3BC0 -#define TCM825X_DG 0x3B3F -#define TCM825X_REJHLEV 0x3CFF -#define TCM825X_ALCLOCK 0x3D80 -#define TCM825X_FPSLNKSW 0x3D40 -#define TCM825X_ALCSPD 0x3D30 -#define TCM825X_REJH 0x3D03 -#define TCM825X_SHESRSW 0x3E80 -#define TCM825X_ESLIMSEL 0x3E40 -#define TCM825X_SHESRSPD 0x3E30 -#define TCM825X_ELSTEP 0x3E0C -#define TCM825X_ELSTART 0x3E03 -#define TCM825X_AGMIN 0x3FFF -#define TCM825X_PREGRG 0x423F -#define TCM825X_PREGBG 0x433F -#define TCM825X_PRERG 0x443F -#define TCM825X_PREBG 0x453F -#define TCM825X_MSKBR 0x477F -#define TCM825X_MSKGR 0x487F -#define TCM825X_MSKRB 0x497F -#define TCM825X_MSKGB 0x4A7F -#define TCM825X_MSKRG 0x4B7F -#define TCM825X_MSKBG 0x4C7F -#define TCM825X_HDTCSW 0x4D80 -#define TCM825X_VDTCSW 0x4D40 -#define TCM825X_DTCYL 0x4D3F -#define TCM825X_HDTPSW 0x4E80 -#define TCM825X_VDTPSW 0x4E40 -#define TCM825X_DTCGAIN 0x4E3F -#define TCM825X_DTLLIMSW 0x4F10 -#define TCM825X_DTLYLIM 0x4F0F -#define TCM825X_YLCUTLMSK 0x5080 -#define TCM825X_YLCUTL 0x503F -#define TCM825X_YLCUTHMSK 0x5180 -#define TCM825X_YLCUTH 0x513F -#define TCM825X_UVSKNC 0x527F -#define TCM825X_UVLJ 0x537F -#define TCM825X_WBGMIN 0x54FF -#define TCM825X_WBGMAX 0x55FF -#define TCM825X_WBSPDUP 0x5603 -#define TCM825X_ALLAREA 0x5820 -#define TCM825X_WBLOCK 0x5810 -#define TCM825X_WB2SP 0x580F -#define TCM825X_KIZUSW 0x5920 -#define TCM825X_PBRSW 0x5910 -#define TCM825X_ABCSW 0x5903 -#define TCM825X_PBDLV 0x5AFF -#define TCM825X_PBC1LV 0x5BFF - -#define TCM825X_NUM_REGS (TCM825X_ADDR(TCM825X_PBC1LV) + 1) - -#define TCM825X_BYTES_PER_PIXEL 2 - -#define TCM825X_REG_TERM 0xff /* terminating list entry for reg */ -#define TCM825X_VAL_TERM 0xff /* terminating list entry for val */ - -/* define a structure for tcm825x register initialization values */ -struct tcm825x_reg { - u8 val; - u16 reg; -}; - -enum image_size { subQCIF = 0, QQVGA, QCIF, QVGA, CIF, VGA }; -enum pixel_format { YUV422 = 0, RGB565 }; -#define NUM_IMAGE_SIZES 6 -#define NUM_PIXEL_FORMATS 2 - -#define TCM825X_XCLK_MIN 11900000 -#define TCM825X_XCLK_MAX 25000000 - -struct capture_size { - unsigned long width; - unsigned long height; -}; - -struct tcm825x_platform_data { - /* Is the sensor usable? Doesn't yet mean it's there, but you - * can try! */ - int (*is_okay)(void); - /* Set power state, zero is off, non-zero is on. */ - int (*power_set)(int power); - /* Default registers written after power-on or reset. */ - const struct tcm825x_reg *(*default_regs)(void); - int (*needs_reset)(struct v4l2_int_device *s, void *buf, - struct v4l2_pix_format *fmt); - int (*ifparm)(struct v4l2_ifparm *p); - int (*is_upside_down)(void); -}; - -/* Array of image sizes supported by TCM825X. These must be ordered from - * smallest image size to largest. - */ -static const struct capture_size tcm825x_sizes[] = { - { 128, 96 }, /* subQCIF */ - { 160, 120 }, /* QQVGA */ - { 176, 144 }, /* QCIF */ - { 320, 240 }, /* QVGA */ - { 352, 288 }, /* CIF */ - { 640, 480 }, /* VGA */ -}; - -#endif /* ifndef TCM825X_H */ diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c deleted file mode 100644 index f7707e65761..00000000000 --- a/drivers/media/video/tda7432.c +++ /dev/null @@ -1,485 +0,0 @@ -/* - * For the STS-Thompson TDA7432 audio processor chip - * - * Handles audio functions: volume, balance, tone, loudness - * This driver will not complain if used with any - * other i2c device with the same address. - * - * Muting and tone control by Jonathan Isom - * - * Copyright (c) 2000 Eric Sandeen - * Copyright (c) 2006 Mauro Carvalho Chehab - * This code is placed under the terms of the GNU General Public License - * Based on tda9855.c by Steve VanDeBogart (vandebo@uclink.berkeley.edu) - * Which was based on tda8425.c by Greg Alexander (c) 1998 - * - * OPTIONS: - * debug - set to 1 if you'd like to see debug messages - * set to 2 if you'd like to be inundated with debug messages - * - * loudness - set between 0 and 15 for varying degrees of loudness effect - * - * maxvol - set maximium volume to +20db (1), default is 0db(0) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#ifndef VIDEO_AUDIO_BALANCE -# define VIDEO_AUDIO_BALANCE 32 -#endif - -MODULE_AUTHOR("Eric Sandeen "); -MODULE_DESCRIPTION("bttv driver for the tda7432 audio processor chip"); -MODULE_LICENSE("GPL"); - -static int maxvol; -static int loudness; /* disable loudness by default */ -static int debug; /* insmod parameter */ -module_param(debug, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Set debugging level from 0 to 3. Default is off(0)."); -module_param(loudness, int, S_IRUGO); -MODULE_PARM_DESC(loudness, "Turn loudness on(1) else off(0). Default is off(0)."); -module_param(maxvol, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(maxvol, "Set maximium volume to +20dB(0) else +0dB(1). Default is +20dB(0)."); - - -/* Structure of address and subaddresses for the tda7432 */ - -struct tda7432 { - struct v4l2_subdev sd; - int addr; - int input; - int volume; - int muted; - int bass, treble; - int lf, lr, rf, rr; - int loud; -}; - -static inline struct tda7432 *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct tda7432, sd); -} - -/* The TDA7432 is made by STS-Thompson - * http://www.st.com - * http://us.st.com/stonline/books/pdf/docs/4056.pdf - * - * TDA7432: I2C-bus controlled basic audio processor - * - * The TDA7432 controls basic audio functions like volume, balance, - * and tone control (including loudness). It also has four channel - * output (for front and rear). Since most vidcap cards probably - * don't have 4 channel output, this driver will set front & rear - * together (no independent control). - */ - - /* Subaddresses for TDA7432 */ - -#define TDA7432_IN 0x00 /* Input select */ -#define TDA7432_VL 0x01 /* Volume */ -#define TDA7432_TN 0x02 /* Bass, Treble (Tone) */ -#define TDA7432_LF 0x03 /* Attenuation LF (Left Front) */ -#define TDA7432_LR 0x04 /* Attenuation LR (Left Rear) */ -#define TDA7432_RF 0x05 /* Attenuation RF (Right Front) */ -#define TDA7432_RR 0x06 /* Attenuation RR (Right Rear) */ -#define TDA7432_LD 0x07 /* Loudness */ - - - /* Masks for bits in TDA7432 subaddresses */ - -/* Many of these not used - just for documentation */ - -/* Subaddress 0x00 - Input selection and bass control */ - -/* Bits 0,1,2 control input: - * 0x00 - Stereo input - * 0x02 - Mono input - * 0x03 - Mute (Using Attenuators Plays better with modules) - * Mono probably isn't used - I'm guessing only the stereo - * input is connected on most cards, so we'll set it to stereo. - * - * Bit 3 controls bass cut: 0/1 is non-symmetric/symmetric bass cut - * Bit 4 controls bass range: 0/1 is extended/standard bass range - * - * Highest 3 bits not used - */ - -#define TDA7432_STEREO_IN 0 -#define TDA7432_MONO_IN 2 /* Probably won't be used */ -#define TDA7432_BASS_SYM 1 << 3 -#define TDA7432_BASS_NORM 1 << 4 - -/* Subaddress 0x01 - Volume */ - -/* Lower 7 bits control volume from -79dB to +32dB in 1dB steps - * Recommended maximum is +20 dB - * - * +32dB: 0x00 - * +20dB: 0x0c - * 0dB: 0x20 - * -79dB: 0x6f - * - * MSB (bit 7) controls loudness: 1/0 is loudness on/off - */ - -#define TDA7432_VOL_0DB 0x20 -#define TDA7432_LD_ON 1 << 7 - - -/* Subaddress 0x02 - Tone control */ - -/* Bits 0,1,2 control absolute treble gain from 0dB to 14dB - * 0x0 is 14dB, 0x7 is 0dB - * - * Bit 3 controls treble attenuation/gain (sign) - * 1 = gain (+) - * 0 = attenuation (-) - * - * Bits 4,5,6 control absolute bass gain from 0dB to 14dB - * (This is only true for normal base range, set in 0x00) - * 0x0 << 4 is 14dB, 0x7 is 0dB - * - * Bit 7 controls bass attenuation/gain (sign) - * 1 << 7 = gain (+) - * 0 << 7 = attenuation (-) - * - * Example: - * 1 1 0 1 0 1 0 1 is +4dB bass, -4dB treble - */ - -#define TDA7432_TREBLE_0DB 0xf -#define TDA7432_TREBLE 7 -#define TDA7432_TREBLE_GAIN 1 << 3 -#define TDA7432_BASS_0DB 0xf -#define TDA7432_BASS 7 << 4 -#define TDA7432_BASS_GAIN 1 << 7 - - -/* Subaddress 0x03 - Left Front attenuation */ -/* Subaddress 0x04 - Left Rear attenuation */ -/* Subaddress 0x05 - Right Front attenuation */ -/* Subaddress 0x06 - Right Rear attenuation */ - -/* Bits 0,1,2,3,4 control attenuation from 0dB to -37.5dB - * in 1.5dB steps. - * - * 0x00 is 0dB - * 0x1f is -37.5dB - * - * Bit 5 mutes that channel when set (1 = mute, 0 = unmute) - * We'll use the mute on the input, though (above) - * Bits 6,7 unused - */ - -#define TDA7432_ATTEN_0DB 0x00 -#define TDA7432_MUTE 0x1 << 5 - - -/* Subaddress 0x07 - Loudness Control */ - -/* Bits 0,1,2,3 control loudness from 0dB to -15dB in 1dB steps - * when bit 4 is NOT set - * - * 0x0 is 0dB - * 0xf is -15dB - * - * If bit 4 is set, then there is a flat attenuation according to - * the lower 4 bits, as above. - * - * Bits 5,6,7 unused - */ - - - -/* Begin code */ - -static int tda7432_write(struct v4l2_subdev *sd, int subaddr, int val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - unsigned char buffer[2]; - - v4l2_dbg(2, debug, sd, "In tda7432_write\n"); - v4l2_dbg(1, debug, sd, "Writing %d 0x%x\n", subaddr, val); - buffer[0] = subaddr; - buffer[1] = val; - if (2 != i2c_master_send(client, buffer, 2)) { - v4l2_err(sd, "I/O error, trying (write %d 0x%x)\n", - subaddr, val); - return -1; - } - return 0; -} - -static int tda7432_set(struct v4l2_subdev *sd) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct tda7432 *t = to_state(sd); - unsigned char buf[16]; - - v4l2_dbg(1, debug, sd, - "tda7432: 7432_set(0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)\n", - t->input, t->volume, t->bass, t->treble, t->lf, t->lr, - t->rf, t->rr, t->loud); - buf[0] = TDA7432_IN; - buf[1] = t->input; - buf[2] = t->volume; - buf[3] = t->bass; - buf[4] = t->treble; - buf[5] = t->lf; - buf[6] = t->lr; - buf[7] = t->rf; - buf[8] = t->rr; - buf[9] = t->loud; - if (10 != i2c_master_send(client, buf, 10)) { - v4l2_err(sd, "I/O error, trying tda7432_set\n"); - return -1; - } - - return 0; -} - -static void do_tda7432_init(struct v4l2_subdev *sd) -{ - struct tda7432 *t = to_state(sd); - - v4l2_dbg(2, debug, sd, "In tda7432_init\n"); - - t->input = TDA7432_STEREO_IN | /* Main (stereo) input */ - TDA7432_BASS_SYM | /* Symmetric bass cut */ - TDA7432_BASS_NORM; /* Normal bass range */ - t->volume = 0x3b ; /* -27dB Volume */ - if (loudness) /* Turn loudness on? */ - t->volume |= TDA7432_LD_ON; - t->muted = 1; - t->treble = TDA7432_TREBLE_0DB; /* 0dB Treble */ - t->bass = TDA7432_BASS_0DB; /* 0dB Bass */ - t->lf = TDA7432_ATTEN_0DB; /* 0dB attenuation */ - t->lr = TDA7432_ATTEN_0DB; /* 0dB attenuation */ - t->rf = TDA7432_ATTEN_0DB; /* 0dB attenuation */ - t->rr = TDA7432_ATTEN_0DB; /* 0dB attenuation */ - t->loud = loudness; /* insmod parameter */ - - tda7432_set(sd); -} - -static int tda7432_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct tda7432 *t = to_state(sd); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value=t->muted; - return 0; - case V4L2_CID_AUDIO_VOLUME: - if (!maxvol){ /* max +20db */ - ctrl->value = ( 0x6f - (t->volume & 0x7F) ) * 630; - } else { /* max 0db */ - ctrl->value = ( 0x6f - (t->volume & 0x7F) ) * 829; - } - return 0; - case V4L2_CID_AUDIO_BALANCE: - { - if ( (t->lf) < (t->rf) ) - /* right is attenuated, balance shifted left */ - ctrl->value = (32768 - 1057*(t->rf)); - else - /* left is attenuated, balance shifted right */ - ctrl->value = (32768 + 1057*(t->lf)); - return 0; - } - case V4L2_CID_AUDIO_BASS: - { - /* Bass/treble 4 bits each */ - int bass=t->bass; - if(bass >= 0x8) - bass = ~(bass - 0x8) & 0xf; - ctrl->value = (bass << 12)+(bass << 8)+(bass << 4)+(bass); - return 0; - } - case V4L2_CID_AUDIO_TREBLE: - { - int treble=t->treble; - if(treble >= 0x8) - treble = ~(treble - 0x8) & 0xf; - ctrl->value = (treble << 12)+(treble << 8)+(treble << 4)+(treble); - return 0; - } - } - return -EINVAL; -} - -static int tda7432_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct tda7432 *t = to_state(sd); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - t->muted=ctrl->value; - break; - case V4L2_CID_AUDIO_VOLUME: - if(!maxvol){ /* max +20db */ - t->volume = 0x6f - ((ctrl->value)/630); - } else { /* max 0db */ - t->volume = 0x6f - ((ctrl->value)/829); - } - if (loudness) /* Turn on the loudness bit */ - t->volume |= TDA7432_LD_ON; - - tda7432_write(sd, TDA7432_VL, t->volume); - return 0; - case V4L2_CID_AUDIO_BALANCE: - if (ctrl->value < 32768) { - /* shifted to left, attenuate right */ - t->rr = (32768 - ctrl->value)/1057; - t->rf = t->rr; - t->lr = TDA7432_ATTEN_0DB; - t->lf = TDA7432_ATTEN_0DB; - } else if(ctrl->value > 32769) { - /* shifted to right, attenuate left */ - t->lf = (ctrl->value - 32768)/1057; - t->lr = t->lf; - t->rr = TDA7432_ATTEN_0DB; - t->rf = TDA7432_ATTEN_0DB; - } else { - /* centered */ - t->rr = TDA7432_ATTEN_0DB; - t->rf = TDA7432_ATTEN_0DB; - t->lf = TDA7432_ATTEN_0DB; - t->lr = TDA7432_ATTEN_0DB; - } - break; - case V4L2_CID_AUDIO_BASS: - t->bass = ctrl->value >> 12; - if(t->bass>= 0x8) - t->bass = (~t->bass & 0xf) + 0x8 ; - - tda7432_write(sd, TDA7432_TN, 0x10 | (t->bass << 4) | t->treble); - return 0; - case V4L2_CID_AUDIO_TREBLE: - t->treble= ctrl->value >> 12; - if(t->treble>= 0x8) - t->treble = (~t->treble & 0xf) + 0x8 ; - - tda7432_write(sd, TDA7432_TN, 0x10 | (t->bass << 4) | t->treble); - return 0; - default: - return -EINVAL; - } - - /* Used for both mute and balance changes */ - if (t->muted) - { - /* Mute & update balance*/ - tda7432_write(sd, TDA7432_LF, t->lf | TDA7432_MUTE); - tda7432_write(sd, TDA7432_LR, t->lr | TDA7432_MUTE); - tda7432_write(sd, TDA7432_RF, t->rf | TDA7432_MUTE); - tda7432_write(sd, TDA7432_RR, t->rr | TDA7432_MUTE); - } else { - tda7432_write(sd, TDA7432_LF, t->lf); - tda7432_write(sd, TDA7432_LR, t->lr); - tda7432_write(sd, TDA7432_RF, t->rf); - tda7432_write(sd, TDA7432_RR, t->rr); - } - return 0; -} - -static int tda7432_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - switch (qc->id) { - case V4L2_CID_AUDIO_VOLUME: - return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880); - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); - } - return -EINVAL; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops tda7432_core_ops = { - .queryctrl = tda7432_queryctrl, - .g_ctrl = tda7432_g_ctrl, - .s_ctrl = tda7432_s_ctrl, -}; - -static const struct v4l2_subdev_ops tda7432_ops = { - .core = &tda7432_core_ops, -}; - -/* ----------------------------------------------------------------------- */ - -/* *********************** * - * i2c interface functions * - * *********************** */ - -static int tda7432_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct tda7432 *t; - struct v4l2_subdev *sd; - - v4l_info(client, "chip found @ 0x%02x (%s)\n", - client->addr << 1, client->adapter->name); - - t = kzalloc(sizeof(*t), GFP_KERNEL); - if (!t) - return -ENOMEM; - sd = &t->sd; - v4l2_i2c_subdev_init(sd, client, &tda7432_ops); - if (loudness < 0 || loudness > 15) { - v4l2_warn(sd, "loudness parameter must be between 0 and 15\n"); - if (loudness < 0) - loudness = 0; - if (loudness > 15) - loudness = 15; - } - - do_tda7432_init(sd); - return 0; -} - -static int tda7432_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - do_tda7432_init(sd); - v4l2_device_unregister_subdev(sd); - kfree(to_state(sd)); - return 0; -} - -static const struct i2c_device_id tda7432_id[] = { - { "tda7432", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tda7432_id); - -static struct i2c_driver tda7432_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "tda7432", - }, - .probe = tda7432_probe, - .remove = tda7432_remove, - .id_table = tda7432_id, -}; - -module_i2c_driver(tda7432_driver); diff --git a/drivers/media/video/tda9840.c b/drivers/media/video/tda9840.c deleted file mode 100644 index 3d7ddd93282..00000000000 --- a/drivers/media/video/tda9840.c +++ /dev/null @@ -1,224 +0,0 @@ - /* - tda9840 - i2c-driver for the tda9840 by SGS Thomson - - Copyright (C) 1998-2003 Michael Hunold - Copyright (C) 2008 Hans Verkuil - - The tda9840 is a stereo/dual sound processor with digital - identification. It can be found at address 0x84 on the i2c-bus. - - For detailed informations download the specifications directly - from SGS Thomson at http://www.st.com - - 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 -#include -#include -#include -#include -#include - -MODULE_AUTHOR("Michael Hunold "); -MODULE_DESCRIPTION("tda9840 driver"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0644); - -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - -#define SWITCH 0x00 -#define LEVEL_ADJUST 0x02 -#define STEREO_ADJUST 0x03 -#define TEST 0x04 - -#define TDA9840_SET_MUTE 0x00 -#define TDA9840_SET_MONO 0x10 -#define TDA9840_SET_STEREO 0x2a -#define TDA9840_SET_LANG1 0x12 -#define TDA9840_SET_LANG2 0x1e -#define TDA9840_SET_BOTH 0x1a -#define TDA9840_SET_BOTH_R 0x16 -#define TDA9840_SET_EXTERNAL 0x7a - - -static void tda9840_write(struct v4l2_subdev *sd, u8 reg, u8 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (i2c_smbus_write_byte_data(client, reg, val)) - v4l2_dbg(1, debug, sd, "error writing %02x to %02x\n", - val, reg); -} - -static int tda9840_status(struct v4l2_subdev *sd) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 byte; - - if (1 != i2c_master_recv(client, &byte, 1)) { - v4l2_dbg(1, debug, sd, - "i2c_master_recv() failed\n"); - return -EIO; - } - - if (byte & 0x80) { - v4l2_dbg(1, debug, sd, - "TDA9840_DETECT: register contents invalid\n"); - return -EINVAL; - } - - v4l2_dbg(1, debug, sd, "TDA9840_DETECT: byte: 0x%02x\n", byte); - return byte & 0x60; -} - -static int tda9840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) -{ - int stat = tda9840_status(sd); - int byte; - - if (t->index) - return -EINVAL; - - stat = stat < 0 ? 0 : stat; - if (stat == 0 || stat == 0x60) /* mono input */ - byte = TDA9840_SET_MONO; - else if (stat == 0x40) /* stereo input */ - byte = (t->audmode == V4L2_TUNER_MODE_MONO) ? - TDA9840_SET_MONO : TDA9840_SET_STEREO; - else { /* bilingual */ - switch (t->audmode) { - case V4L2_TUNER_MODE_LANG1_LANG2: - byte = TDA9840_SET_BOTH; - break; - case V4L2_TUNER_MODE_LANG2: - byte = TDA9840_SET_LANG2; - break; - default: - byte = TDA9840_SET_LANG1; - break; - } - } - v4l2_dbg(1, debug, sd, "TDA9840_SWITCH: 0x%02x\n", byte); - tda9840_write(sd, SWITCH, byte); - return 0; -} - -static int tda9840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) -{ - int stat = tda9840_status(sd); - - if (stat < 0) - return stat; - - t->rxsubchans = V4L2_TUNER_SUB_MONO; - - switch (stat & 0x60) { - case 0x00: - t->rxsubchans = V4L2_TUNER_SUB_MONO; - break; - case 0x20: - t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - break; - case 0x40: - t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; - break; - default: /* Incorrect detect */ - t->rxsubchans = V4L2_TUNER_MODE_MONO; - break; - } - return 0; -} - -static int tda9840_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TDA9840, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops tda9840_core_ops = { - .g_chip_ident = tda9840_g_chip_ident, -}; - -static const struct v4l2_subdev_tuner_ops tda9840_tuner_ops = { - .s_tuner = tda9840_s_tuner, - .g_tuner = tda9840_g_tuner, -}; - -static const struct v4l2_subdev_ops tda9840_ops = { - .core = &tda9840_core_ops, - .tuner = &tda9840_tuner_ops, -}; - -/* ----------------------------------------------------------------------- */ - -static int tda9840_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct v4l2_subdev *sd; - - /* let's see whether this adapter can support what we need */ - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_READ_BYTE_DATA | - I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) - return -EIO; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); - if (sd == NULL) - return -ENOMEM; - v4l2_i2c_subdev_init(sd, client, &tda9840_ops); - - /* set initial values for level & stereo - adjustment, mode */ - tda9840_write(sd, LEVEL_ADJUST, 0); - tda9840_write(sd, STEREO_ADJUST, 0); - tda9840_write(sd, SWITCH, TDA9840_SET_STEREO); - return 0; -} - -static int tda9840_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - kfree(sd); - return 0; -} - -static const struct i2c_device_id tda9840_id[] = { - { "tda9840", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tda9840_id); - -static struct i2c_driver tda9840_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "tda9840", - }, - .probe = tda9840_probe, - .remove = tda9840_remove, - .id_table = tda9840_id, -}; - -module_i2c_driver(tda9840_driver); diff --git a/drivers/media/video/tea6415c.c b/drivers/media/video/tea6415c.c deleted file mode 100644 index d1d6ea1dd27..00000000000 --- a/drivers/media/video/tea6415c.c +++ /dev/null @@ -1,187 +0,0 @@ - /* - tea6415c - i2c-driver for the tea6415c by SGS Thomson - - Copyright (C) 1998-2003 Michael Hunold - Copyright (C) 2008 Hans Verkuil - - The tea6415c is a bus controlled video-matrix-switch - with 8 inputs and 6 outputs. - It is cascadable, i.e. it can be found at the addresses - 0x86 and 0x06 on the i2c-bus. - - For detailed informations download the specifications directly - from SGS Thomson at http://www.st.com - - 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 -#include -#include -#include -#include -#include -#include "tea6415c.h" - -MODULE_AUTHOR("Michael Hunold "); -MODULE_DESCRIPTION("tea6415c driver"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0644); - -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - - -/* makes a connection between the input-pin 'i' and the output-pin 'o' */ -static int tea6415c_s_routing(struct v4l2_subdev *sd, - u32 i, u32 o, u32 config) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 byte = 0; - int ret; - - v4l2_dbg(1, debug, sd, "i=%d, o=%d\n", i, o); - - /* check if the pins are valid */ - if (0 == ((1 == i || 3 == i || 5 == i || 6 == i || 8 == i || 10 == i || 20 == i || 11 == i) - && (18 == o || 17 == o || 16 == o || 15 == o || 14 == o || 13 == o))) - return -EINVAL; - - /* to understand this, have a look at the tea6415c-specs (p.5) */ - switch (o) { - case 18: - byte = 0x00; - break; - case 14: - byte = 0x20; - break; - case 16: - byte = 0x10; - break; - case 17: - byte = 0x08; - break; - case 15: - byte = 0x18; - break; - case 13: - byte = 0x28; - break; - }; - - switch (i) { - case 5: - byte |= 0x00; - break; - case 8: - byte |= 0x04; - break; - case 3: - byte |= 0x02; - break; - case 20: - byte |= 0x06; - break; - case 6: - byte |= 0x01; - break; - case 10: - byte |= 0x05; - break; - case 1: - byte |= 0x03; - break; - case 11: - byte |= 0x07; - break; - }; - - ret = i2c_smbus_write_byte(client, byte); - if (ret) { - v4l2_dbg(1, debug, sd, - "i2c_smbus_write_byte() failed, ret:%d\n", ret); - return -EIO; - } - return ret; -} - -static int tea6415c_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEA6415C, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops tea6415c_core_ops = { - .g_chip_ident = tea6415c_g_chip_ident, -}; - -static const struct v4l2_subdev_video_ops tea6415c_video_ops = { - .s_routing = tea6415c_s_routing, -}; - -static const struct v4l2_subdev_ops tea6415c_ops = { - .core = &tea6415c_core_ops, - .video = &tea6415c_video_ops, -}; - -static int tea6415c_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct v4l2_subdev *sd; - - /* let's see whether this adapter can support what we need */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE)) - return -EIO; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); - if (sd == NULL) - return -ENOMEM; - v4l2_i2c_subdev_init(sd, client, &tea6415c_ops); - return 0; -} - -static int tea6415c_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - kfree(sd); - return 0; -} - -static const struct i2c_device_id tea6415c_id[] = { - { "tea6415c", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tea6415c_id); - -static struct i2c_driver tea6415c_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "tea6415c", - }, - .probe = tea6415c_probe, - .remove = tea6415c_remove, - .id_table = tea6415c_id, -}; - -module_i2c_driver(tea6415c_driver); diff --git a/drivers/media/video/tea6415c.h b/drivers/media/video/tea6415c.h deleted file mode 100644 index 3a47d697536..00000000000 --- a/drivers/media/video/tea6415c.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef __INCLUDED_TEA6415C__ -#define __INCLUDED_TEA6415C__ - -/* the tea6415c's design is quite brain-dead. although there are - 8 inputs and 6 outputs, these aren't enumerated in any way. because - I don't want to say "connect input pin 20 to output pin 17", I define - a "virtual" pin-order. */ - -/* input pins */ -#define TEA6415C_OUTPUT1 18 -#define TEA6415C_OUTPUT2 14 -#define TEA6415C_OUTPUT3 16 -#define TEA6415C_OUTPUT4 17 -#define TEA6415C_OUTPUT5 13 -#define TEA6415C_OUTPUT6 15 - -/* output pins */ -#define TEA6415C_INPUT1 5 -#define TEA6415C_INPUT2 8 -#define TEA6415C_INPUT3 3 -#define TEA6415C_INPUT4 20 -#define TEA6415C_INPUT5 6 -#define TEA6415C_INPUT6 10 -#define TEA6415C_INPUT7 1 -#define TEA6415C_INPUT8 11 - -#endif diff --git a/drivers/media/video/tea6420.c b/drivers/media/video/tea6420.c deleted file mode 100644 index 38757217a07..00000000000 --- a/drivers/media/video/tea6420.c +++ /dev/null @@ -1,169 +0,0 @@ - /* - tea6420 - i2c-driver for the tea6420 by SGS Thomson - - Copyright (C) 1998-2003 Michael Hunold - Copyright (C) 2008 Hans Verkuil - - The tea6420 is a bus controlled audio-matrix with 5 stereo inputs, - 4 stereo outputs and gain control for each output. - It is cascadable, i.e. it can be found at the addresses 0x98 - and 0x9a on the i2c-bus. - - For detailed informations download the specifications directly - from SGS Thomson at http://www.st.com - - 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 -#include -#include -#include -#include -#include -#include "tea6420.h" - -MODULE_AUTHOR("Michael Hunold "); -MODULE_DESCRIPTION("tea6420 driver"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0644); - -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - - -/* make a connection between the input 'i' and the output 'o' - with gain 'g' (note: i = 6 means 'mute') */ -static int tea6420_s_routing(struct v4l2_subdev *sd, - u32 i, u32 o, u32 config) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int g = (o >> 4) & 0xf; - u8 byte; - int ret; - - o &= 0xf; - v4l2_dbg(1, debug, sd, "i=%d, o=%d, g=%d\n", i, o, g); - - /* check if the parameters are valid */ - if (i < 1 || i > 6 || o < 1 || o > 4 || g < 0 || g > 6 || g % 2 != 0) - return -EINVAL; - - byte = ((o - 1) << 5); - byte |= (i - 1); - - /* to understand this, have a look at the tea6420-specs (p.5) */ - switch (g) { - case 0: - byte |= (3 << 3); - break; - case 2: - byte |= (2 << 3); - break; - case 4: - byte |= (1 << 3); - break; - case 6: - break; - } - - ret = i2c_smbus_write_byte(client, byte); - if (ret) { - v4l2_dbg(1, debug, sd, - "i2c_smbus_write_byte() failed, ret:%d\n", ret); - return -EIO; - } - return 0; -} - -static int tea6420_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEA6420, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops tea6420_core_ops = { - .g_chip_ident = tea6420_g_chip_ident, -}; - -static const struct v4l2_subdev_audio_ops tea6420_audio_ops = { - .s_routing = tea6420_s_routing, -}; - -static const struct v4l2_subdev_ops tea6420_ops = { - .core = &tea6420_core_ops, - .audio = &tea6420_audio_ops, -}; - -static int tea6420_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct v4l2_subdev *sd; - int err, i; - - /* let's see whether this adapter can support what we need */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE)) - return -EIO; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); - if (sd == NULL) - return -ENOMEM; - v4l2_i2c_subdev_init(sd, client, &tea6420_ops); - - /* set initial values: set "mute"-input to all outputs at gain 0 */ - err = 0; - for (i = 1; i < 5; i++) - err += tea6420_s_routing(sd, 6, i, 0); - if (err) { - v4l_dbg(1, debug, client, "could not initialize tea6420\n"); - return -ENODEV; - } - return 0; -} - -static int tea6420_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - kfree(sd); - return 0; -} - -static const struct i2c_device_id tea6420_id[] = { - { "tea6420", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tea6420_id); - -static struct i2c_driver tea6420_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "tea6420", - }, - .probe = tea6420_probe, - .remove = tea6420_remove, - .id_table = tea6420_id, -}; - -module_i2c_driver(tea6420_driver); diff --git a/drivers/media/video/tea6420.h b/drivers/media/video/tea6420.h deleted file mode 100644 index 4aa3edb3e19..00000000000 --- a/drivers/media/video/tea6420.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef __INCLUDED_TEA6420__ -#define __INCLUDED_TEA6420__ - -/* input pins */ -#define TEA6420_OUTPUT1 1 -#define TEA6420_OUTPUT2 2 -#define TEA6420_OUTPUT3 3 -#define TEA6420_OUTPUT4 4 - -/* output pins */ -#define TEA6420_INPUT1 1 -#define TEA6420_INPUT2 2 -#define TEA6420_INPUT3 3 -#define TEA6420_INPUT4 4 -#define TEA6420_INPUT5 5 -#define TEA6420_INPUT6 6 - -/* gain on the output pins, ORed with the output pin */ -#define TEA6420_GAIN0 0x00 -#define TEA6420_GAIN2 0x20 -#define TEA6420_GAIN4 0x40 -#define TEA6420_GAIN6 0x60 - -#endif diff --git a/drivers/media/video/ths7303.c b/drivers/media/video/ths7303.c deleted file mode 100644 index e5c0eedebc5..00000000000 --- a/drivers/media/video/ths7303.c +++ /dev/null @@ -1,140 +0,0 @@ -/* - * ths7303- THS7303 Video Amplifier driver - * - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ - * - * 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 .as is. WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -MODULE_DESCRIPTION("TI THS7303 video amplifier driver"); -MODULE_AUTHOR("Chaithrika U S"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug level 0-1"); - -/* following function is used to set ths7303 */ -static int ths7303_setvalue(struct v4l2_subdev *sd, v4l2_std_id std) -{ - int err = 0; - u8 val; - struct i2c_client *client; - - client = v4l2_get_subdevdata(sd); - - if (std & (V4L2_STD_ALL & ~V4L2_STD_SECAM)) { - val = 0x02; - v4l2_dbg(1, debug, sd, "setting value for SDTV format\n"); - } else { - val = 0x00; - v4l2_dbg(1, debug, sd, "disabling all channels\n"); - } - - err |= i2c_smbus_write_byte_data(client, 0x01, val); - err |= i2c_smbus_write_byte_data(client, 0x02, val); - err |= i2c_smbus_write_byte_data(client, 0x03, val); - - if (err) - v4l2_err(sd, "write failed\n"); - - return err; -} - -static int ths7303_s_std_output(struct v4l2_subdev *sd, v4l2_std_id norm) -{ - return ths7303_setvalue(sd, norm); -} - -static int ths7303_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_THS7303, 0); -} - -static const struct v4l2_subdev_video_ops ths7303_video_ops = { - .s_std_output = ths7303_s_std_output, -}; - -static const struct v4l2_subdev_core_ops ths7303_core_ops = { - .g_chip_ident = ths7303_g_chip_ident, -}; - -static const struct v4l2_subdev_ops ths7303_ops = { - .core = &ths7303_core_ops, - .video = &ths7303_video_ops, -}; - -static int ths7303_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct v4l2_subdev *sd; - v4l2_std_id std_id = V4L2_STD_NTSC; - - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); - if (sd == NULL) - return -ENOMEM; - - v4l2_i2c_subdev_init(sd, client, &ths7303_ops); - - return ths7303_setvalue(sd, std_id); -} - -static int ths7303_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - kfree(sd); - - return 0; -} - -static const struct i2c_device_id ths7303_id[] = { - {"ths7303", 0}, - {}, -}; - -MODULE_DEVICE_TABLE(i2c, ths7303_id); - -static struct i2c_driver ths7303_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "ths7303", - }, - .probe = ths7303_probe, - .remove = ths7303_remove, - .id_table = ths7303_id, -}; - -module_i2c_driver(ths7303_driver); diff --git a/drivers/media/video/tlv320aic23b.c b/drivers/media/video/tlv320aic23b.c deleted file mode 100644 index 809a75a558e..00000000000 --- a/drivers/media/video/tlv320aic23b.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - * tlv320aic23b - driver version 0.0.1 - * - * Copyright (C) 2006 Scott Alfter - * - * Based on wm8775 driver - * - * Copyright (C) 2004 Ulf Eklund - * Copyright (C) 2005 Hans Verkuil - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("tlv320aic23b driver"); -MODULE_AUTHOR("Scott Alfter, Ulf Eklund, Hans Verkuil"); -MODULE_LICENSE("GPL"); - - -/* ----------------------------------------------------------------------- */ - -struct tlv320aic23b_state { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; -}; - -static inline struct tlv320aic23b_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct tlv320aic23b_state, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct tlv320aic23b_state, hdl)->sd; -} - -static int tlv320aic23b_write(struct v4l2_subdev *sd, int reg, u16 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int i; - - if ((reg < 0 || reg > 9) && (reg != 15)) { - v4l2_err(sd, "Invalid register R%d\n", reg); - return -1; - } - - for (i = 0; i < 3; i++) - if (i2c_smbus_write_byte_data(client, - (reg << 1) | (val >> 8), val & 0xff) == 0) - return 0; - v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg); - return -1; -} - -static int tlv320aic23b_s_clock_freq(struct v4l2_subdev *sd, u32 freq) -{ - switch (freq) { - case 32000: /* set sample rate to 32 kHz */ - tlv320aic23b_write(sd, 8, 0x018); - break; - case 44100: /* set sample rate to 44.1 kHz */ - tlv320aic23b_write(sd, 8, 0x022); - break; - case 48000: /* set sample rate to 48 kHz */ - tlv320aic23b_write(sd, 8, 0x000); - break; - default: - return -EINVAL; - } - return 0; -} - -static int tlv320aic23b_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - tlv320aic23b_write(sd, 0, 0x180); /* mute both channels */ - /* set gain on both channels to +3.0 dB */ - if (!ctrl->val) - tlv320aic23b_write(sd, 0, 0x119); - return 0; - } - return -EINVAL; -} - -static int tlv320aic23b_log_status(struct v4l2_subdev *sd) -{ - struct tlv320aic23b_state *state = to_state(sd); - - v4l2_ctrl_handler_log_status(&state->hdl, sd->name); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_ctrl_ops tlv320aic23b_ctrl_ops = { - .s_ctrl = tlv320aic23b_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops tlv320aic23b_core_ops = { - .log_status = tlv320aic23b_log_status, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, -}; - -static const struct v4l2_subdev_audio_ops tlv320aic23b_audio_ops = { - .s_clock_freq = tlv320aic23b_s_clock_freq, -}; - -static const struct v4l2_subdev_ops tlv320aic23b_ops = { - .core = &tlv320aic23b_core_ops, - .audio = &tlv320aic23b_audio_ops, -}; - -/* ----------------------------------------------------------------------- */ - -/* i2c implementation */ - -/* - * Generic i2c probe - * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' - */ - -static int tlv320aic23b_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct tlv320aic23b_state *state; - struct v4l2_subdev *sd; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - state = kzalloc(sizeof(struct tlv320aic23b_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &tlv320aic23b_ops); - - /* Initialize tlv320aic23b */ - - /* RESET */ - tlv320aic23b_write(sd, 15, 0x000); - /* turn off DAC & mic input */ - tlv320aic23b_write(sd, 6, 0x00A); - /* left-justified, 24-bit, master mode */ - tlv320aic23b_write(sd, 7, 0x049); - /* set gain on both channels to +3.0 dB */ - tlv320aic23b_write(sd, 0, 0x119); - /* set sample rate to 48 kHz */ - tlv320aic23b_write(sd, 8, 0x000); - /* activate digital interface */ - tlv320aic23b_write(sd, 9, 0x001); - - v4l2_ctrl_handler_init(&state->hdl, 1); - v4l2_ctrl_new_std(&state->hdl, &tlv320aic23b_ctrl_ops, - V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); - sd->ctrl_handler = &state->hdl; - if (state->hdl.error) { - int err = state->hdl.error; - - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return err; - } - v4l2_ctrl_handler_setup(&state->hdl); - return 0; -} - -static int tlv320aic23b_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct tlv320aic23b_state *state = to_state(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id tlv320aic23b_id[] = { - { "tlv320aic23b", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tlv320aic23b_id); - -static struct i2c_driver tlv320aic23b_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "tlv320aic23b", - }, - .probe = tlv320aic23b_probe, - .remove = tlv320aic23b_remove, - .id_table = tlv320aic23b_id, -}; - -module_i2c_driver(tlv320aic23b_driver); diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c deleted file mode 100644 index 321b3153df8..00000000000 --- a/drivers/media/video/tvaudio.c +++ /dev/null @@ -1,2118 +0,0 @@ -/* - * Driver for simple i2c audio chips. - * - * Copyright (c) 2000 Gerd Knorr - * based on code by: - * Eric Sandeen (eric_sandeen@bigfoot.com) - * Steve VanDeBogart (vandebo@uclink.berkeley.edu) - * Greg Alexander (galexand@acm.org) - * - * Copyright(c) 2005-2008 Mauro Carvalho Chehab - * - Some cleanups, code fixes, etc - * - Convert it to V4L2 API - * - * This code is placed under the terms of the GNU General Public License - * - * OPTIONS: - * debug - set to 1 if you'd like to see debug messages - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -/* ---------------------------------------------------------------------- */ -/* insmod args */ - -static int debug; /* insmod parameter */ -module_param(debug, int, 0644); - -MODULE_DESCRIPTION("device driver for various i2c TV sound decoder / audiomux chips"); -MODULE_AUTHOR("Eric Sandeen, Steve VanDeBogart, Greg Alexander, Gerd Knorr"); -MODULE_LICENSE("GPL"); - -#define UNSET (-1U) - -/* ---------------------------------------------------------------------- */ -/* our structs */ - -#define MAXREGS 256 - -struct CHIPSTATE; -typedef int (*getvalue)(int); -typedef int (*checkit)(struct CHIPSTATE*); -typedef int (*initialize)(struct CHIPSTATE*); -typedef int (*getrxsubchans)(struct CHIPSTATE *); -typedef void (*setaudmode)(struct CHIPSTATE*, int mode); - -/* i2c command */ -typedef struct AUDIOCMD { - int count; /* # of bytes to send */ - unsigned char bytes[MAXREGS+1]; /* addr, data, data, ... */ -} audiocmd; - -/* chip description */ -struct CHIPDESC { - char *name; /* chip name */ - int addr_lo, addr_hi; /* i2c address range */ - int registers; /* # of registers */ - - int *insmodopt; - checkit checkit; - initialize initialize; - int flags; -#define CHIP_HAS_VOLUME 1 -#define CHIP_HAS_BASSTREBLE 2 -#define CHIP_HAS_INPUTSEL 4 -#define CHIP_NEED_CHECKMODE 8 - - /* various i2c command sequences */ - audiocmd init; - - /* which register has which value */ - int leftreg,rightreg,treblereg,bassreg; - - /* initialize with (defaults to 65535/65535/32768/32768 */ - int leftinit,rightinit,trebleinit,bassinit; - - /* functions to convert the values (v4l -> chip) */ - getvalue volfunc,treblefunc,bassfunc; - - /* get/set mode */ - getrxsubchans getrxsubchans; - setaudmode setaudmode; - - /* input switch register + values for v4l inputs */ - int inputreg; - int inputmap[4]; - int inputmute; - int inputmask; -}; - -/* current state of the chip */ -struct CHIPSTATE { - struct v4l2_subdev sd; - - /* chip-specific description - should point to - an entry at CHIPDESC table */ - struct CHIPDESC *desc; - - /* shadow register set */ - audiocmd shadow; - - /* current settings */ - __u16 left, right, treble, bass, muted; - int prevmode; - int radio; - int input; - - /* thread */ - struct task_struct *thread; - struct timer_list wt; - int audmode; -}; - -static inline struct CHIPSTATE *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct CHIPSTATE, sd); -} - - -/* ---------------------------------------------------------------------- */ -/* i2c I/O functions */ - -static int chip_write(struct CHIPSTATE *chip, int subaddr, int val) -{ - struct v4l2_subdev *sd = &chip->sd; - struct i2c_client *c = v4l2_get_subdevdata(sd); - unsigned char buffer[2]; - - if (subaddr < 0) { - v4l2_dbg(1, debug, sd, "chip_write: 0x%x\n", val); - chip->shadow.bytes[1] = val; - buffer[0] = val; - if (1 != i2c_master_send(c, buffer, 1)) { - v4l2_warn(sd, "I/O error (write 0x%x)\n", val); - return -1; - } - } else { - if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) { - v4l2_info(sd, - "Tried to access a non-existent register: %d\n", - subaddr); - return -EINVAL; - } - - v4l2_dbg(1, debug, sd, "chip_write: reg%d=0x%x\n", - subaddr, val); - chip->shadow.bytes[subaddr+1] = val; - buffer[0] = subaddr; - buffer[1] = val; - if (2 != i2c_master_send(c, buffer, 2)) { - v4l2_warn(sd, "I/O error (write reg%d=0x%x)\n", - subaddr, val); - return -1; - } - } - return 0; -} - -static int chip_write_masked(struct CHIPSTATE *chip, - int subaddr, int val, int mask) -{ - struct v4l2_subdev *sd = &chip->sd; - - if (mask != 0) { - if (subaddr < 0) { - val = (chip->shadow.bytes[1] & ~mask) | (val & mask); - } else { - if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) { - v4l2_info(sd, - "Tried to access a non-existent register: %d\n", - subaddr); - return -EINVAL; - } - - val = (chip->shadow.bytes[subaddr+1] & ~mask) | (val & mask); - } - } - return chip_write(chip, subaddr, val); -} - -static int chip_read(struct CHIPSTATE *chip) -{ - struct v4l2_subdev *sd = &chip->sd; - struct i2c_client *c = v4l2_get_subdevdata(sd); - unsigned char buffer; - - if (1 != i2c_master_recv(c, &buffer, 1)) { - v4l2_warn(sd, "I/O error (read)\n"); - return -1; - } - v4l2_dbg(1, debug, sd, "chip_read: 0x%x\n", buffer); - return buffer; -} - -static int chip_read2(struct CHIPSTATE *chip, int subaddr) -{ - struct v4l2_subdev *sd = &chip->sd; - struct i2c_client *c = v4l2_get_subdevdata(sd); - unsigned char write[1]; - unsigned char read[1]; - struct i2c_msg msgs[2] = { - { c->addr, 0, 1, write }, - { c->addr, I2C_M_RD, 1, read } - }; - - write[0] = subaddr; - - if (2 != i2c_transfer(c->adapter, msgs, 2)) { - v4l2_warn(sd, "I/O error (read2)\n"); - return -1; - } - v4l2_dbg(1, debug, sd, "chip_read2: reg%d=0x%x\n", - subaddr, read[0]); - return read[0]; -} - -static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd) -{ - struct v4l2_subdev *sd = &chip->sd; - struct i2c_client *c = v4l2_get_subdevdata(sd); - int i; - - if (0 == cmd->count) - return 0; - - if (cmd->count + cmd->bytes[0] - 1 >= ARRAY_SIZE(chip->shadow.bytes)) { - v4l2_info(sd, - "Tried to access a non-existent register range: %d to %d\n", - cmd->bytes[0] + 1, cmd->bytes[0] + cmd->count - 1); - return -EINVAL; - } - - /* FIXME: it seems that the shadow bytes are wrong bellow !*/ - - /* update our shadow register set; print bytes if (debug > 0) */ - v4l2_dbg(1, debug, sd, "chip_cmd(%s): reg=%d, data:", - name, cmd->bytes[0]); - for (i = 1; i < cmd->count; i++) { - if (debug) - printk(KERN_CONT " 0x%x", cmd->bytes[i]); - chip->shadow.bytes[i+cmd->bytes[0]] = cmd->bytes[i]; - } - if (debug) - printk(KERN_CONT "\n"); - - /* send data to the chip */ - if (cmd->count != i2c_master_send(c, cmd->bytes, cmd->count)) { - v4l2_warn(sd, "I/O error (%s)\n", name); - return -1; - } - return 0; -} - -/* ---------------------------------------------------------------------- */ -/* kernel thread for doing i2c stuff asyncronly - * right now it is used only to check the audio mode (mono/stereo/whatever) - * some time after switching to another TV channel, then turn on stereo - * if available, ... - */ - -static void chip_thread_wake(unsigned long data) -{ - struct CHIPSTATE *chip = (struct CHIPSTATE*)data; - wake_up_process(chip->thread); -} - -static int chip_thread(void *data) -{ - struct CHIPSTATE *chip = data; - struct CHIPDESC *desc = chip->desc; - struct v4l2_subdev *sd = &chip->sd; - int mode, selected; - - v4l2_dbg(1, debug, sd, "thread started\n"); - set_freezable(); - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - if (!kthread_should_stop()) - schedule(); - set_current_state(TASK_RUNNING); - try_to_freeze(); - if (kthread_should_stop()) - break; - v4l2_dbg(1, debug, sd, "thread wakeup\n"); - - /* don't do anything for radio */ - if (chip->radio) - continue; - - /* have a look what's going on */ - mode = desc->getrxsubchans(chip); - if (mode == chip->prevmode) - continue; - - /* chip detected a new audio mode - set it */ - v4l2_dbg(1, debug, sd, "thread checkmode\n"); - - chip->prevmode = mode; - - selected = V4L2_TUNER_MODE_MONO; - switch (chip->audmode) { - case V4L2_TUNER_MODE_MONO: - if (mode & V4L2_TUNER_SUB_LANG1) - selected = V4L2_TUNER_MODE_LANG1; - break; - case V4L2_TUNER_MODE_STEREO: - case V4L2_TUNER_MODE_LANG1: - if (mode & V4L2_TUNER_SUB_LANG1) - selected = V4L2_TUNER_MODE_LANG1; - else if (mode & V4L2_TUNER_SUB_STEREO) - selected = V4L2_TUNER_MODE_STEREO; - break; - case V4L2_TUNER_MODE_LANG2: - if (mode & V4L2_TUNER_SUB_LANG2) - selected = V4L2_TUNER_MODE_LANG2; - else if (mode & V4L2_TUNER_SUB_STEREO) - selected = V4L2_TUNER_MODE_STEREO; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - if (mode & V4L2_TUNER_SUB_LANG2) - selected = V4L2_TUNER_MODE_LANG1_LANG2; - else if (mode & V4L2_TUNER_SUB_STEREO) - selected = V4L2_TUNER_MODE_STEREO; - } - desc->setaudmode(chip, selected); - - /* schedule next check */ - mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000)); - } - - v4l2_dbg(1, debug, sd, "thread exiting\n"); - return 0; -} - -/* ---------------------------------------------------------------------- */ -/* audio chip descriptions - defines+functions for tda9840 */ - -#define TDA9840_SW 0x00 -#define TDA9840_LVADJ 0x02 -#define TDA9840_STADJ 0x03 -#define TDA9840_TEST 0x04 - -#define TDA9840_MONO 0x10 -#define TDA9840_STEREO 0x2a -#define TDA9840_DUALA 0x12 -#define TDA9840_DUALB 0x1e -#define TDA9840_DUALAB 0x1a -#define TDA9840_DUALBA 0x16 -#define TDA9840_EXTERNAL 0x7a - -#define TDA9840_DS_DUAL 0x20 /* Dual sound identified */ -#define TDA9840_ST_STEREO 0x40 /* Stereo sound identified */ -#define TDA9840_PONRES 0x80 /* Power-on reset detected if = 1 */ - -#define TDA9840_TEST_INT1SN 0x1 /* Integration time 0.5s when set */ -#define TDA9840_TEST_INTFU 0x02 /* Disables integrator function */ - -static int tda9840_getrxsubchans(struct CHIPSTATE *chip) -{ - struct v4l2_subdev *sd = &chip->sd; - int val, mode; - - val = chip_read(chip); - mode = V4L2_TUNER_SUB_MONO; - if (val & TDA9840_DS_DUAL) - mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - if (val & TDA9840_ST_STEREO) - mode = V4L2_TUNER_SUB_STEREO; - - v4l2_dbg(1, debug, sd, - "tda9840_getrxsubchans(): raw chip read: %d, return: %d\n", - val, mode); - return mode; -} - -static void tda9840_setaudmode(struct CHIPSTATE *chip, int mode) -{ - int update = 1; - int t = chip->shadow.bytes[TDA9840_SW + 1] & ~0x7e; - - switch (mode) { - case V4L2_TUNER_MODE_MONO: - t |= TDA9840_MONO; - break; - case V4L2_TUNER_MODE_STEREO: - t |= TDA9840_STEREO; - break; - case V4L2_TUNER_MODE_LANG1: - t |= TDA9840_DUALA; - break; - case V4L2_TUNER_MODE_LANG2: - t |= TDA9840_DUALB; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - t |= TDA9840_DUALAB; - break; - default: - update = 0; - } - - if (update) - chip_write(chip, TDA9840_SW, t); -} - -static int tda9840_checkit(struct CHIPSTATE *chip) -{ - int rc; - rc = chip_read(chip); - /* lower 5 bits should be 0 */ - return ((rc & 0x1f) == 0) ? 1 : 0; -} - -/* ---------------------------------------------------------------------- */ -/* audio chip descriptions - defines+functions for tda985x */ - -/* subaddresses for TDA9855 */ -#define TDA9855_VR 0x00 /* Volume, right */ -#define TDA9855_VL 0x01 /* Volume, left */ -#define TDA9855_BA 0x02 /* Bass */ -#define TDA9855_TR 0x03 /* Treble */ -#define TDA9855_SW 0x04 /* Subwoofer - not connected on DTV2000 */ - -/* subaddresses for TDA9850 */ -#define TDA9850_C4 0x04 /* Control 1 for TDA9850 */ - -/* subaddesses for both chips */ -#define TDA985x_C5 0x05 /* Control 2 for TDA9850, Control 1 for TDA9855 */ -#define TDA985x_C6 0x06 /* Control 3 for TDA9850, Control 2 for TDA9855 */ -#define TDA985x_C7 0x07 /* Control 4 for TDA9850, Control 3 for TDA9855 */ -#define TDA985x_A1 0x08 /* Alignment 1 for both chips */ -#define TDA985x_A2 0x09 /* Alignment 2 for both chips */ -#define TDA985x_A3 0x0a /* Alignment 3 for both chips */ - -/* Masks for bits in TDA9855 subaddresses */ -/* 0x00 - VR in TDA9855 */ -/* 0x01 - VL in TDA9855 */ -/* lower 7 bits control gain from -71dB (0x28) to 16dB (0x7f) - * in 1dB steps - mute is 0x27 */ - - -/* 0x02 - BA in TDA9855 */ -/* lower 5 bits control bass gain from -12dB (0x06) to 16.5dB (0x19) - * in .5dB steps - 0 is 0x0E */ - - -/* 0x03 - TR in TDA9855 */ -/* 4 bits << 1 control treble gain from -12dB (0x3) to 12dB (0xb) - * in 3dB steps - 0 is 0x7 */ - -/* Masks for bits in both chips' subaddresses */ -/* 0x04 - SW in TDA9855, C4/Control 1 in TDA9850 */ -/* Unique to TDA9855: */ -/* 4 bits << 2 control subwoofer/surround gain from -14db (0x1) to 14db (0xf) - * in 3dB steps - mute is 0x0 */ - -/* Unique to TDA9850: */ -/* lower 4 bits control stereo noise threshold, over which stereo turns off - * set to values of 0x00 through 0x0f for Ster1 through Ster16 */ - - -/* 0x05 - C5 - Control 1 in TDA9855 , Control 2 in TDA9850*/ -/* Unique to TDA9855: */ -#define TDA9855_MUTE 1<<7 /* GMU, Mute at outputs */ -#define TDA9855_AVL 1<<6 /* AVL, Automatic Volume Level */ -#define TDA9855_LOUD 1<<5 /* Loudness, 1==off */ -#define TDA9855_SUR 1<<3 /* Surround / Subwoofer 1==.5(L-R) 0==.5(L+R) */ - /* Bits 0 to 3 select various combinations - * of line in and line out, only the - * interesting ones are defined */ -#define TDA9855_EXT 1<<2 /* Selects inputs LIR and LIL. Pins 41 & 12 */ -#define TDA9855_INT 0 /* Selects inputs LOR and LOL. (internal) */ - -/* Unique to TDA9850: */ -/* lower 4 bits contol SAP noise threshold, over which SAP turns off - * set to values of 0x00 through 0x0f for SAP1 through SAP16 */ - - -/* 0x06 - C6 - Control 2 in TDA9855, Control 3 in TDA9850 */ -/* Common to TDA9855 and TDA9850: */ -#define TDA985x_SAP 3<<6 /* Selects SAP output, mute if not received */ -#define TDA985x_MONOSAP 2<<6 /* Selects Mono on left, SAP on right */ -#define TDA985x_STEREO 1<<6 /* Selects Stereo ouput, mono if not received */ -#define TDA985x_MONO 0 /* Forces Mono output */ -#define TDA985x_LMU 1<<3 /* Mute (LOR/LOL for 9855, OUTL/OUTR for 9850) */ - -/* Unique to TDA9855: */ -#define TDA9855_TZCM 1<<5 /* If set, don't mute till zero crossing */ -#define TDA9855_VZCM 1<<4 /* If set, don't change volume till zero crossing*/ -#define TDA9855_LINEAR 0 /* Linear Stereo */ -#define TDA9855_PSEUDO 1 /* Pseudo Stereo */ -#define TDA9855_SPAT_30 2 /* Spatial Stereo, 30% anti-phase crosstalk */ -#define TDA9855_SPAT_50 3 /* Spatial Stereo, 52% anti-phase crosstalk */ -#define TDA9855_E_MONO 7 /* Forced mono - mono select elseware, so useless*/ - -/* 0x07 - C7 - Control 3 in TDA9855, Control 4 in TDA9850 */ -/* Common to both TDA9855 and TDA9850: */ -/* lower 4 bits control input gain from -3.5dB (0x0) to 4dB (0xF) - * in .5dB steps - 0dB is 0x7 */ - -/* 0x08, 0x09 - A1 and A2 (read/write) */ -/* Common to both TDA9855 and TDA9850: */ -/* lower 5 bites are wideband and spectral expander alignment - * from 0x00 to 0x1f - nominal at 0x0f and 0x10 (read/write) */ -#define TDA985x_STP 1<<5 /* Stereo Pilot/detect (read-only) */ -#define TDA985x_SAPP 1<<6 /* SAP Pilot/detect (read-only) */ -#define TDA985x_STS 1<<7 /* Stereo trigger 1= <35mV 0= <30mV (write-only)*/ - -/* 0x0a - A3 */ -/* Common to both TDA9855 and TDA9850: */ -/* lower 3 bits control timing current for alignment: -30% (0x0), -20% (0x1), - * -10% (0x2), nominal (0x3), +10% (0x6), +20% (0x5), +30% (0x4) */ -#define TDA985x_ADJ 1<<7 /* Stereo adjust on/off (wideband and spectral */ - -static int tda9855_volume(int val) { return val/0x2e8+0x27; } -static int tda9855_bass(int val) { return val/0xccc+0x06; } -static int tda9855_treble(int val) { return (val/0x1c71+0x3)<<1; } - -static int tda985x_getrxsubchans(struct CHIPSTATE *chip) -{ - int mode, val; - - /* Add mono mode regardless of SAP and stereo */ - /* Allows forced mono */ - mode = V4L2_TUNER_SUB_MONO; - val = chip_read(chip); - if (val & TDA985x_STP) - mode = V4L2_TUNER_SUB_STEREO; - if (val & TDA985x_SAPP) - mode |= V4L2_TUNER_SUB_SAP; - return mode; -} - -static void tda985x_setaudmode(struct CHIPSTATE *chip, int mode) -{ - int update = 1; - int c6 = chip->shadow.bytes[TDA985x_C6+1] & 0x3f; - - switch (mode) { - case V4L2_TUNER_MODE_MONO: - c6 |= TDA985x_MONO; - break; - case V4L2_TUNER_MODE_STEREO: - case V4L2_TUNER_MODE_LANG1: - c6 |= TDA985x_STEREO; - break; - case V4L2_TUNER_MODE_SAP: - c6 |= TDA985x_SAP; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - c6 |= TDA985x_MONOSAP; - break; - default: - update = 0; - } - if (update) - chip_write(chip,TDA985x_C6,c6); -} - - -/* ---------------------------------------------------------------------- */ -/* audio chip descriptions - defines+functions for tda9873h */ - -/* Subaddresses for TDA9873H */ - -#define TDA9873_SW 0x00 /* Switching */ -#define TDA9873_AD 0x01 /* Adjust */ -#define TDA9873_PT 0x02 /* Port */ - -/* Subaddress 0x00: Switching Data - * B7..B0: - * - * B1, B0: Input source selection - * 0, 0 internal - * 1, 0 external stereo - * 0, 1 external mono - */ -#define TDA9873_INP_MASK 3 -#define TDA9873_INTERNAL 0 -#define TDA9873_EXT_STEREO 2 -#define TDA9873_EXT_MONO 1 - -/* B3, B2: output signal select - * B4 : transmission mode - * 0, 0, 1 Mono - * 1, 0, 0 Stereo - * 1, 1, 1 Stereo (reversed channel) - * 0, 0, 0 Dual AB - * 0, 0, 1 Dual AA - * 0, 1, 0 Dual BB - * 0, 1, 1 Dual BA - */ - -#define TDA9873_TR_MASK (7 << 2) -#define TDA9873_TR_MONO 4 -#define TDA9873_TR_STEREO 1 << 4 -#define TDA9873_TR_REVERSE ((1 << 3) | (1 << 2)) -#define TDA9873_TR_DUALA 1 << 2 -#define TDA9873_TR_DUALB 1 << 3 -#define TDA9873_TR_DUALAB 0 - -/* output level controls - * B5: output level switch (0 = reduced gain, 1 = normal gain) - * B6: mute (1 = muted) - * B7: auto-mute (1 = auto-mute enabled) - */ - -#define TDA9873_GAIN_NORMAL 1 << 5 -#define TDA9873_MUTE 1 << 6 -#define TDA9873_AUTOMUTE 1 << 7 - -/* Subaddress 0x01: Adjust/standard */ - -/* Lower 4 bits (C3..C0) control stereo adjustment on R channel (-0.6 - +0.7 dB) - * Recommended value is +0 dB - */ - -#define TDA9873_STEREO_ADJ 0x06 /* 0dB gain */ - -/* Bits C6..C4 control FM stantard - * C6, C5, C4 - * 0, 0, 0 B/G (PAL FM) - * 0, 0, 1 M - * 0, 1, 0 D/K(1) - * 0, 1, 1 D/K(2) - * 1, 0, 0 D/K(3) - * 1, 0, 1 I - */ -#define TDA9873_BG 0 -#define TDA9873_M 1 -#define TDA9873_DK1 2 -#define TDA9873_DK2 3 -#define TDA9873_DK3 4 -#define TDA9873_I 5 - -/* C7 controls identification response time (1=fast/0=normal) - */ -#define TDA9873_IDR_NORM 0 -#define TDA9873_IDR_FAST 1 << 7 - - -/* Subaddress 0x02: Port data */ - -/* E1, E0 free programmable ports P1/P2 - 0, 0 both ports low - 0, 1 P1 high - 1, 0 P2 high - 1, 1 both ports high -*/ - -#define TDA9873_PORTS 3 - -/* E2: test port */ -#define TDA9873_TST_PORT 1 << 2 - -/* E5..E3 control mono output channel (together with transmission mode bit B4) - * - * E5 E4 E3 B4 OUTM - * 0 0 0 0 mono - * 0 0 1 0 DUAL B - * 0 1 0 1 mono (from stereo decoder) - */ -#define TDA9873_MOUT_MONO 0 -#define TDA9873_MOUT_FMONO 0 -#define TDA9873_MOUT_DUALA 0 -#define TDA9873_MOUT_DUALB 1 << 3 -#define TDA9873_MOUT_ST 1 << 4 -#define TDA9873_MOUT_EXTM ((1 << 4) | (1 << 3)) -#define TDA9873_MOUT_EXTL 1 << 5 -#define TDA9873_MOUT_EXTR ((1 << 5) | (1 << 3)) -#define TDA9873_MOUT_EXTLR ((1 << 5) | (1 << 4)) -#define TDA9873_MOUT_MUTE ((1 << 5) | (1 << 4) | (1 << 3)) - -/* Status bits: (chip read) */ -#define TDA9873_PONR 0 /* Power-on reset detected if = 1 */ -#define TDA9873_STEREO 2 /* Stereo sound is identified */ -#define TDA9873_DUAL 4 /* Dual sound is identified */ - -static int tda9873_getrxsubchans(struct CHIPSTATE *chip) -{ - struct v4l2_subdev *sd = &chip->sd; - int val,mode; - - val = chip_read(chip); - mode = V4L2_TUNER_SUB_MONO; - if (val & TDA9873_STEREO) - mode = V4L2_TUNER_SUB_STEREO; - if (val & TDA9873_DUAL) - mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - v4l2_dbg(1, debug, sd, - "tda9873_getrxsubchans(): raw chip read: %d, return: %d\n", - val, mode); - return mode; -} - -static void tda9873_setaudmode(struct CHIPSTATE *chip, int mode) -{ - struct v4l2_subdev *sd = &chip->sd; - int sw_data = chip->shadow.bytes[TDA9873_SW+1] & ~ TDA9873_TR_MASK; - /* int adj_data = chip->shadow.bytes[TDA9873_AD+1] ; */ - - if ((sw_data & TDA9873_INP_MASK) != TDA9873_INTERNAL) { - v4l2_dbg(1, debug, sd, - "tda9873_setaudmode(): external input\n"); - return; - } - - v4l2_dbg(1, debug, sd, - "tda9873_setaudmode(): chip->shadow.bytes[%d] = %d\n", - TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]); - v4l2_dbg(1, debug, sd, "tda9873_setaudmode(): sw_data = %d\n", - sw_data); - - switch (mode) { - case V4L2_TUNER_MODE_MONO: - sw_data |= TDA9873_TR_MONO; - break; - case V4L2_TUNER_MODE_STEREO: - sw_data |= TDA9873_TR_STEREO; - break; - case V4L2_TUNER_MODE_LANG1: - sw_data |= TDA9873_TR_DUALA; - break; - case V4L2_TUNER_MODE_LANG2: - sw_data |= TDA9873_TR_DUALB; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - sw_data |= TDA9873_TR_DUALAB; - break; - default: - return; - } - - chip_write(chip, TDA9873_SW, sw_data); - v4l2_dbg(1, debug, sd, - "tda9873_setaudmode(): req. mode %d; chip_write: %d\n", - mode, sw_data); -} - -static int tda9873_checkit(struct CHIPSTATE *chip) -{ - int rc; - - if (-1 == (rc = chip_read2(chip,254))) - return 0; - return (rc & ~0x1f) == 0x80; -} - - -/* ---------------------------------------------------------------------- */ -/* audio chip description - defines+functions for tda9874h and tda9874a */ -/* Dariusz Kowalewski */ - -/* Subaddresses for TDA9874H and TDA9874A (slave rx) */ -#define TDA9874A_AGCGR 0x00 /* AGC gain */ -#define TDA9874A_GCONR 0x01 /* general config */ -#define TDA9874A_MSR 0x02 /* monitor select */ -#define TDA9874A_C1FRA 0x03 /* carrier 1 freq. */ -#define TDA9874A_C1FRB 0x04 /* carrier 1 freq. */ -#define TDA9874A_C1FRC 0x05 /* carrier 1 freq. */ -#define TDA9874A_C2FRA 0x06 /* carrier 2 freq. */ -#define TDA9874A_C2FRB 0x07 /* carrier 2 freq. */ -#define TDA9874A_C2FRC 0x08 /* carrier 2 freq. */ -#define TDA9874A_DCR 0x09 /* demodulator config */ -#define TDA9874A_FMER 0x0a /* FM de-emphasis */ -#define TDA9874A_FMMR 0x0b /* FM dematrix */ -#define TDA9874A_C1OLAR 0x0c /* ch.1 output level adj. */ -#define TDA9874A_C2OLAR 0x0d /* ch.2 output level adj. */ -#define TDA9874A_NCONR 0x0e /* NICAM config */ -#define TDA9874A_NOLAR 0x0f /* NICAM output level adj. */ -#define TDA9874A_NLELR 0x10 /* NICAM lower error limit */ -#define TDA9874A_NUELR 0x11 /* NICAM upper error limit */ -#define TDA9874A_AMCONR 0x12 /* audio mute control */ -#define TDA9874A_SDACOSR 0x13 /* stereo DAC output select */ -#define TDA9874A_AOSR 0x14 /* analog output select */ -#define TDA9874A_DAICONR 0x15 /* digital audio interface config */ -#define TDA9874A_I2SOSR 0x16 /* I2S-bus output select */ -#define TDA9874A_I2SOLAR 0x17 /* I2S-bus output level adj. */ -#define TDA9874A_MDACOSR 0x18 /* mono DAC output select (tda9874a) */ -#define TDA9874A_ESP 0xFF /* easy standard progr. (tda9874a) */ - -/* Subaddresses for TDA9874H and TDA9874A (slave tx) */ -#define TDA9874A_DSR 0x00 /* device status */ -#define TDA9874A_NSR 0x01 /* NICAM status */ -#define TDA9874A_NECR 0x02 /* NICAM error count */ -#define TDA9874A_DR1 0x03 /* add. data LSB */ -#define TDA9874A_DR2 0x04 /* add. data MSB */ -#define TDA9874A_LLRA 0x05 /* monitor level read-out LSB */ -#define TDA9874A_LLRB 0x06 /* monitor level read-out MSB */ -#define TDA9874A_SIFLR 0x07 /* SIF level */ -#define TDA9874A_TR2 252 /* test reg. 2 */ -#define TDA9874A_TR1 253 /* test reg. 1 */ -#define TDA9874A_DIC 254 /* device id. code */ -#define TDA9874A_SIC 255 /* software id. code */ - - -static int tda9874a_mode = 1; /* 0: A2, 1: NICAM */ -static int tda9874a_GCONR = 0xc0; /* default config. input pin: SIFSEL=0 */ -static int tda9874a_NCONR = 0x01; /* default NICAM config.: AMSEL=0,AMUTE=1 */ -static int tda9874a_ESP = 0x07; /* default standard: NICAM D/K */ -static int tda9874a_dic = -1; /* device id. code */ - -/* insmod options for tda9874a */ -static unsigned int tda9874a_SIF = UNSET; -static unsigned int tda9874a_AMSEL = UNSET; -static unsigned int tda9874a_STD = UNSET; -module_param(tda9874a_SIF, int, 0444); -module_param(tda9874a_AMSEL, int, 0444); -module_param(tda9874a_STD, int, 0444); - -/* - * initialization table for tda9874 decoder: - * - carrier 1 freq. registers (3 bytes) - * - carrier 2 freq. registers (3 bytes) - * - demudulator config register - * - FM de-emphasis register (slow identification mode) - * Note: frequency registers must be written in single i2c transfer. - */ -static struct tda9874a_MODES { - char *name; - audiocmd cmd; -} tda9874a_modelist[9] = { - { "A2, B/G", /* default */ - { 9, { TDA9874A_C1FRA, 0x72,0x95,0x55, 0x77,0xA0,0x00, 0x00,0x00 }} }, - { "A2, M (Korea)", - { 9, { TDA9874A_C1FRA, 0x5D,0xC0,0x00, 0x62,0x6A,0xAA, 0x20,0x22 }} }, - { "A2, D/K (1)", - { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x82,0x60,0x00, 0x00,0x00 }} }, - { "A2, D/K (2)", - { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x8C,0x75,0x55, 0x00,0x00 }} }, - { "A2, D/K (3)", - { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x77,0xA0,0x00, 0x00,0x00 }} }, - { "NICAM, I", - { 9, { TDA9874A_C1FRA, 0x7D,0x00,0x00, 0x88,0x8A,0xAA, 0x08,0x33 }} }, - { "NICAM, B/G", - { 9, { TDA9874A_C1FRA, 0x72,0x95,0x55, 0x79,0xEA,0xAA, 0x08,0x33 }} }, - { "NICAM, D/K", - { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x79,0xEA,0xAA, 0x08,0x33 }} }, - { "NICAM, L", - { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x79,0xEA,0xAA, 0x09,0x33 }} } -}; - -static int tda9874a_setup(struct CHIPSTATE *chip) -{ - struct v4l2_subdev *sd = &chip->sd; - - chip_write(chip, TDA9874A_AGCGR, 0x00); /* 0 dB */ - chip_write(chip, TDA9874A_GCONR, tda9874a_GCONR); - chip_write(chip, TDA9874A_MSR, (tda9874a_mode) ? 0x03:0x02); - if(tda9874a_dic == 0x11) { - chip_write(chip, TDA9874A_FMMR, 0x80); - } else { /* dic == 0x07 */ - chip_cmd(chip,"tda9874_modelist",&tda9874a_modelist[tda9874a_STD].cmd); - chip_write(chip, TDA9874A_FMMR, 0x00); - } - chip_write(chip, TDA9874A_C1OLAR, 0x00); /* 0 dB */ - chip_write(chip, TDA9874A_C2OLAR, 0x00); /* 0 dB */ - chip_write(chip, TDA9874A_NCONR, tda9874a_NCONR); - chip_write(chip, TDA9874A_NOLAR, 0x00); /* 0 dB */ - /* Note: If signal quality is poor you may want to change NICAM */ - /* error limit registers (NLELR and NUELR) to some greater values. */ - /* Then the sound would remain stereo, but won't be so clear. */ - chip_write(chip, TDA9874A_NLELR, 0x14); /* default */ - chip_write(chip, TDA9874A_NUELR, 0x50); /* default */ - - if(tda9874a_dic == 0x11) { - chip_write(chip, TDA9874A_AMCONR, 0xf9); - chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80); - chip_write(chip, TDA9874A_AOSR, 0x80); - chip_write(chip, TDA9874A_MDACOSR, (tda9874a_mode) ? 0x82:0x80); - chip_write(chip, TDA9874A_ESP, tda9874a_ESP); - } else { /* dic == 0x07 */ - chip_write(chip, TDA9874A_AMCONR, 0xfb); - chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80); - chip_write(chip, TDA9874A_AOSR, 0x00); /* or 0x10 */ - } - v4l2_dbg(1, debug, sd, "tda9874a_setup(): %s [0x%02X].\n", - tda9874a_modelist[tda9874a_STD].name,tda9874a_STD); - return 1; -} - -static int tda9874a_getrxsubchans(struct CHIPSTATE *chip) -{ - struct v4l2_subdev *sd = &chip->sd; - int dsr,nsr,mode; - int necr; /* just for debugging */ - - mode = V4L2_TUNER_SUB_MONO; - - if(-1 == (dsr = chip_read2(chip,TDA9874A_DSR))) - return mode; - if(-1 == (nsr = chip_read2(chip,TDA9874A_NSR))) - return mode; - if(-1 == (necr = chip_read2(chip,TDA9874A_NECR))) - return mode; - - /* need to store dsr/nsr somewhere */ - chip->shadow.bytes[MAXREGS-2] = dsr; - chip->shadow.bytes[MAXREGS-1] = nsr; - - if(tda9874a_mode) { - /* Note: DSR.RSSF and DSR.AMSTAT bits are also checked. - * If NICAM auto-muting is enabled, DSR.AMSTAT=1 indicates - * that sound has (temporarily) switched from NICAM to - * mono FM (or AM) on 1st sound carrier due to high NICAM bit - * error count. So in fact there is no stereo in this case :-( - * But changing the mode to V4L2_TUNER_MODE_MONO would switch - * external 4052 multiplexer in audio_hook(). - */ - if(nsr & 0x02) /* NSR.S/MB=1 */ - mode = V4L2_TUNER_SUB_STEREO; - if(nsr & 0x01) /* NSR.D/SB=1 */ - mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - } else { - if(dsr & 0x02) /* DSR.IDSTE=1 */ - mode = V4L2_TUNER_SUB_STEREO; - if(dsr & 0x04) /* DSR.IDDUA=1 */ - mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - } - - v4l2_dbg(1, debug, sd, - "tda9874a_getrxsubchans(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n", - dsr, nsr, necr, mode); - return mode; -} - -static void tda9874a_setaudmode(struct CHIPSTATE *chip, int mode) -{ - struct v4l2_subdev *sd = &chip->sd; - - /* Disable/enable NICAM auto-muting (based on DSR.RSSF status bit). */ - /* If auto-muting is disabled, we can hear a signal of degrading quality. */ - if (tda9874a_mode) { - if(chip->shadow.bytes[MAXREGS-2] & 0x20) /* DSR.RSSF=1 */ - tda9874a_NCONR &= 0xfe; /* enable */ - else - tda9874a_NCONR |= 0x01; /* disable */ - chip_write(chip, TDA9874A_NCONR, tda9874a_NCONR); - } - - /* Note: TDA9874A supports automatic FM dematrixing (FMMR register) - * and has auto-select function for audio output (AOSR register). - * Old TDA9874H doesn't support these features. - * TDA9874A also has additional mono output pin (OUTM), which - * on same (all?) tv-cards is not used, anyway (as well as MONOIN). - */ - if(tda9874a_dic == 0x11) { - int aosr = 0x80; - int mdacosr = (tda9874a_mode) ? 0x82:0x80; - - switch(mode) { - case V4L2_TUNER_MODE_MONO: - case V4L2_TUNER_MODE_STEREO: - break; - case V4L2_TUNER_MODE_LANG1: - aosr = 0x80; /* auto-select, dual A/A */ - mdacosr = (tda9874a_mode) ? 0x82:0x80; - break; - case V4L2_TUNER_MODE_LANG2: - aosr = 0xa0; /* auto-select, dual B/B */ - mdacosr = (tda9874a_mode) ? 0x83:0x81; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - aosr = 0x00; /* always route L to L and R to R */ - mdacosr = (tda9874a_mode) ? 0x82:0x80; - break; - default: - return; - } - chip_write(chip, TDA9874A_AOSR, aosr); - chip_write(chip, TDA9874A_MDACOSR, mdacosr); - - v4l2_dbg(1, debug, sd, - "tda9874a_setaudmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n", - mode, aosr, mdacosr); - - } else { /* dic == 0x07 */ - int fmmr,aosr; - - switch(mode) { - case V4L2_TUNER_MODE_MONO: - fmmr = 0x00; /* mono */ - aosr = 0x10; /* A/A */ - break; - case V4L2_TUNER_MODE_STEREO: - if(tda9874a_mode) { - fmmr = 0x00; - aosr = 0x00; /* handled by NICAM auto-mute */ - } else { - fmmr = (tda9874a_ESP == 1) ? 0x05 : 0x04; /* stereo */ - aosr = 0x00; - } - break; - case V4L2_TUNER_MODE_LANG1: - fmmr = 0x02; /* dual */ - aosr = 0x10; /* dual A/A */ - break; - case V4L2_TUNER_MODE_LANG2: - fmmr = 0x02; /* dual */ - aosr = 0x20; /* dual B/B */ - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - fmmr = 0x02; /* dual */ - aosr = 0x00; /* dual A/B */ - break; - default: - return; - } - chip_write(chip, TDA9874A_FMMR, fmmr); - chip_write(chip, TDA9874A_AOSR, aosr); - - v4l2_dbg(1, debug, sd, - "tda9874a_setaudmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n", - mode, fmmr, aosr); - } -} - -static int tda9874a_checkit(struct CHIPSTATE *chip) -{ - struct v4l2_subdev *sd = &chip->sd; - int dic,sic; /* device id. and software id. codes */ - - if(-1 == (dic = chip_read2(chip,TDA9874A_DIC))) - return 0; - if(-1 == (sic = chip_read2(chip,TDA9874A_SIC))) - return 0; - - v4l2_dbg(1, debug, sd, "tda9874a_checkit(): DIC=0x%X, SIC=0x%X.\n", dic, sic); - - if((dic == 0x11)||(dic == 0x07)) { - v4l2_info(sd, "found tda9874%s.\n", (dic == 0x11) ? "a" : "h"); - tda9874a_dic = dic; /* remember device id. */ - return 1; - } - return 0; /* not found */ -} - -static int tda9874a_initialize(struct CHIPSTATE *chip) -{ - if (tda9874a_SIF > 2) - tda9874a_SIF = 1; - if (tda9874a_STD >= ARRAY_SIZE(tda9874a_modelist)) - tda9874a_STD = 0; - if(tda9874a_AMSEL > 1) - tda9874a_AMSEL = 0; - - if(tda9874a_SIF == 1) - tda9874a_GCONR = 0xc0; /* sound IF input 1 */ - else - tda9874a_GCONR = 0xc1; /* sound IF input 2 */ - - tda9874a_ESP = tda9874a_STD; - tda9874a_mode = (tda9874a_STD < 5) ? 0 : 1; - - if(tda9874a_AMSEL == 0) - tda9874a_NCONR = 0x01; /* auto-mute: analog mono input */ - else - tda9874a_NCONR = 0x05; /* auto-mute: 1st carrier FM or AM */ - - tda9874a_setup(chip); - return 0; -} - -/* ---------------------------------------------------------------------- */ -/* audio chip description - defines+functions for tda9875 */ -/* The TDA9875 is made by Philips Semiconductor - * http://www.semiconductors.philips.com - * TDA9875: I2C-bus controlled DSP audio processor, FM demodulator - * - */ - -/* subaddresses for TDA9875 */ -#define TDA9875_MUT 0x12 /*General mute (value --> 0b11001100*/ -#define TDA9875_CFG 0x01 /* Config register (value --> 0b00000000 */ -#define TDA9875_DACOS 0x13 /*DAC i/o select (ADC) 0b0000100*/ -#define TDA9875_LOSR 0x16 /*Line output select regirter 0b0100 0001*/ - -#define TDA9875_CH1V 0x0c /*Channel 1 volume (mute)*/ -#define TDA9875_CH2V 0x0d /*Channel 2 volume (mute)*/ -#define TDA9875_SC1 0x14 /*SCART 1 in (mono)*/ -#define TDA9875_SC2 0x15 /*SCART 2 in (mono)*/ - -#define TDA9875_ADCIS 0x17 /*ADC input select (mono) 0b0110 000*/ -#define TDA9875_AER 0x19 /*Audio effect (AVL+Pseudo) 0b0000 0110*/ -#define TDA9875_MCS 0x18 /*Main channel select (DAC) 0b0000100*/ -#define TDA9875_MVL 0x1a /* Main volume gauche */ -#define TDA9875_MVR 0x1b /* Main volume droite */ -#define TDA9875_MBA 0x1d /* Main Basse */ -#define TDA9875_MTR 0x1e /* Main treble */ -#define TDA9875_ACS 0x1f /* Auxiliary channel select (FM) 0b0000000*/ -#define TDA9875_AVL 0x20 /* Auxiliary volume gauche */ -#define TDA9875_AVR 0x21 /* Auxiliary volume droite */ -#define TDA9875_ABA 0x22 /* Auxiliary Basse */ -#define TDA9875_ATR 0x23 /* Auxiliary treble */ - -#define TDA9875_MSR 0x02 /* Monitor select register */ -#define TDA9875_C1MSB 0x03 /* Carrier 1 (FM) frequency register MSB */ -#define TDA9875_C1MIB 0x04 /* Carrier 1 (FM) frequency register (16-8]b */ -#define TDA9875_C1LSB 0x05 /* Carrier 1 (FM) frequency register LSB */ -#define TDA9875_C2MSB 0x06 /* Carrier 2 (nicam) frequency register MSB */ -#define TDA9875_C2MIB 0x07 /* Carrier 2 (nicam) frequency register (16-8]b */ -#define TDA9875_C2LSB 0x08 /* Carrier 2 (nicam) frequency register LSB */ -#define TDA9875_DCR 0x09 /* Demodulateur configuration regirter*/ -#define TDA9875_DEEM 0x0a /* FM de-emphasis regirter*/ -#define TDA9875_FMAT 0x0b /* FM Matrix regirter*/ - -/* values */ -#define TDA9875_MUTE_ON 0xff /* general mute */ -#define TDA9875_MUTE_OFF 0xcc /* general no mute */ - -static int tda9875_initialize(struct CHIPSTATE *chip) -{ - chip_write(chip, TDA9875_CFG, 0xd0); /*reg de config 0 (reset)*/ - chip_write(chip, TDA9875_MSR, 0x03); /* Monitor 0b00000XXX*/ - chip_write(chip, TDA9875_C1MSB, 0x00); /*Car1(FM) MSB XMHz*/ - chip_write(chip, TDA9875_C1MIB, 0x00); /*Car1(FM) MIB XMHz*/ - chip_write(chip, TDA9875_C1LSB, 0x00); /*Car1(FM) LSB XMHz*/ - chip_write(chip, TDA9875_C2MSB, 0x00); /*Car2(NICAM) MSB XMHz*/ - chip_write(chip, TDA9875_C2MIB, 0x00); /*Car2(NICAM) MIB XMHz*/ - chip_write(chip, TDA9875_C2LSB, 0x00); /*Car2(NICAM) LSB XMHz*/ - chip_write(chip, TDA9875_DCR, 0x00); /*Demod config 0x00*/ - chip_write(chip, TDA9875_DEEM, 0x44); /*DE-Emph 0b0100 0100*/ - chip_write(chip, TDA9875_FMAT, 0x00); /*FM Matrix reg 0x00*/ - chip_write(chip, TDA9875_SC1, 0x00); /* SCART 1 (SC1)*/ - chip_write(chip, TDA9875_SC2, 0x01); /* SCART 2 (sc2)*/ - - chip_write(chip, TDA9875_CH1V, 0x10); /* Channel volume 1 mute*/ - chip_write(chip, TDA9875_CH2V, 0x10); /* Channel volume 2 mute */ - chip_write(chip, TDA9875_DACOS, 0x02); /* sig DAC i/o(in:nicam)*/ - chip_write(chip, TDA9875_ADCIS, 0x6f); /* sig ADC input(in:mono)*/ - chip_write(chip, TDA9875_LOSR, 0x00); /* line out (in:mono)*/ - chip_write(chip, TDA9875_AER, 0x00); /*06 Effect (AVL+PSEUDO) */ - chip_write(chip, TDA9875_MCS, 0x44); /* Main ch select (DAC) */ - chip_write(chip, TDA9875_MVL, 0x03); /* Vol Main left 10dB */ - chip_write(chip, TDA9875_MVR, 0x03); /* Vol Main right 10dB*/ - chip_write(chip, TDA9875_MBA, 0x00); /* Main Bass Main 0dB*/ - chip_write(chip, TDA9875_MTR, 0x00); /* Main Treble Main 0dB*/ - chip_write(chip, TDA9875_ACS, 0x44); /* Aux chan select (dac)*/ - chip_write(chip, TDA9875_AVL, 0x00); /* Vol Aux left 0dB*/ - chip_write(chip, TDA9875_AVR, 0x00); /* Vol Aux right 0dB*/ - chip_write(chip, TDA9875_ABA, 0x00); /* Aux Bass Main 0dB*/ - chip_write(chip, TDA9875_ATR, 0x00); /* Aux Aigus Main 0dB*/ - - chip_write(chip, TDA9875_MUT, 0xcc); /* General mute */ - return 0; -} - -static int tda9875_volume(int val) { return (unsigned char)(val / 602 - 84); } -static int tda9875_bass(int val) { return (unsigned char)(max(-12, val / 2115 - 15)); } -static int tda9875_treble(int val) { return (unsigned char)(val / 2622 - 12); } - -/* ----------------------------------------------------------------------- */ - - -/* *********************** * - * i2c interface functions * - * *********************** */ - -static int tda9875_checkit(struct CHIPSTATE *chip) -{ - struct v4l2_subdev *sd = &chip->sd; - int dic, rev; - - dic = chip_read2(chip, 254); - rev = chip_read2(chip, 255); - - if (dic == 0 || dic == 2) { /* tda9875 and tda9875A */ - v4l2_info(sd, "found tda9875%s rev. %d.\n", - dic == 0 ? "" : "A", rev); - return 1; - } - return 0; -} - -/* ---------------------------------------------------------------------- */ -/* audio chip descriptions - defines+functions for tea6420 */ - -#define TEA6300_VL 0x00 /* volume left */ -#define TEA6300_VR 0x01 /* volume right */ -#define TEA6300_BA 0x02 /* bass */ -#define TEA6300_TR 0x03 /* treble */ -#define TEA6300_FA 0x04 /* fader control */ -#define TEA6300_S 0x05 /* switch register */ - /* values for those registers: */ -#define TEA6300_S_SA 0x01 /* stereo A input */ -#define TEA6300_S_SB 0x02 /* stereo B */ -#define TEA6300_S_SC 0x04 /* stereo C */ -#define TEA6300_S_GMU 0x80 /* general mute */ - -#define TEA6320_V 0x00 /* volume (0-5)/loudness off (6)/zero crossing mute(7) */ -#define TEA6320_FFR 0x01 /* fader front right (0-5) */ -#define TEA6320_FFL 0x02 /* fader front left (0-5) */ -#define TEA6320_FRR 0x03 /* fader rear right (0-5) */ -#define TEA6320_FRL 0x04 /* fader rear left (0-5) */ -#define TEA6320_BA 0x05 /* bass (0-4) */ -#define TEA6320_TR 0x06 /* treble (0-4) */ -#define TEA6320_S 0x07 /* switch register */ - /* values for those registers: */ -#define TEA6320_S_SA 0x07 /* stereo A input */ -#define TEA6320_S_SB 0x06 /* stereo B */ -#define TEA6320_S_SC 0x05 /* stereo C */ -#define TEA6320_S_SD 0x04 /* stereo D */ -#define TEA6320_S_GMU 0x80 /* general mute */ - -#define TEA6420_S_SA 0x00 /* stereo A input */ -#define TEA6420_S_SB 0x01 /* stereo B */ -#define TEA6420_S_SC 0x02 /* stereo C */ -#define TEA6420_S_SD 0x03 /* stereo D */ -#define TEA6420_S_SE 0x04 /* stereo E */ -#define TEA6420_S_GMU 0x05 /* general mute */ - -static int tea6300_shift10(int val) { return val >> 10; } -static int tea6300_shift12(int val) { return val >> 12; } - -/* Assumes 16bit input (values 0x3f to 0x0c are unique, values less than */ -/* 0x0c mirror those immediately higher) */ -static int tea6320_volume(int val) { return (val / (65535/(63-12)) + 12) & 0x3f; } -static int tea6320_shift11(int val) { return val >> 11; } -static int tea6320_initialize(struct CHIPSTATE * chip) -{ - chip_write(chip, TEA6320_FFR, 0x3f); - chip_write(chip, TEA6320_FFL, 0x3f); - chip_write(chip, TEA6320_FRR, 0x3f); - chip_write(chip, TEA6320_FRL, 0x3f); - - return 0; -} - - -/* ---------------------------------------------------------------------- */ -/* audio chip descriptions - defines+functions for tda8425 */ - -#define TDA8425_VL 0x00 /* volume left */ -#define TDA8425_VR 0x01 /* volume right */ -#define TDA8425_BA 0x02 /* bass */ -#define TDA8425_TR 0x03 /* treble */ -#define TDA8425_S1 0x08 /* switch functions */ - /* values for those registers: */ -#define TDA8425_S1_OFF 0xEE /* audio off (mute on) */ -#define TDA8425_S1_CH1 0xCE /* audio channel 1 (mute off) - "linear stereo" mode */ -#define TDA8425_S1_CH2 0xCF /* audio channel 2 (mute off) - "linear stereo" mode */ -#define TDA8425_S1_MU 0x20 /* mute bit */ -#define TDA8425_S1_STEREO 0x18 /* stereo bits */ -#define TDA8425_S1_STEREO_SPATIAL 0x18 /* spatial stereo */ -#define TDA8425_S1_STEREO_LINEAR 0x08 /* linear stereo */ -#define TDA8425_S1_STEREO_PSEUDO 0x10 /* pseudo stereo */ -#define TDA8425_S1_STEREO_MONO 0x00 /* forced mono */ -#define TDA8425_S1_ML 0x06 /* language selector */ -#define TDA8425_S1_ML_SOUND_A 0x02 /* sound a */ -#define TDA8425_S1_ML_SOUND_B 0x04 /* sound b */ -#define TDA8425_S1_ML_STEREO 0x06 /* stereo */ -#define TDA8425_S1_IS 0x01 /* channel selector */ - - -static int tda8425_shift10(int val) { return (val >> 10) | 0xc0; } -static int tda8425_shift12(int val) { return (val >> 12) | 0xf0; } - -static void tda8425_setaudmode(struct CHIPSTATE *chip, int mode) -{ - int s1 = chip->shadow.bytes[TDA8425_S1+1] & 0xe1; - - switch (mode) { - case V4L2_TUNER_MODE_LANG1: - s1 |= TDA8425_S1_ML_SOUND_A; - s1 |= TDA8425_S1_STEREO_PSEUDO; - break; - case V4L2_TUNER_MODE_LANG2: - s1 |= TDA8425_S1_ML_SOUND_B; - s1 |= TDA8425_S1_STEREO_PSEUDO; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - s1 |= TDA8425_S1_ML_STEREO; - s1 |= TDA8425_S1_STEREO_LINEAR; - break; - case V4L2_TUNER_MODE_MONO: - s1 |= TDA8425_S1_ML_STEREO; - s1 |= TDA8425_S1_STEREO_MONO; - break; - case V4L2_TUNER_MODE_STEREO: - s1 |= TDA8425_S1_ML_STEREO; - s1 |= TDA8425_S1_STEREO_SPATIAL; - break; - default: - return; - } - chip_write(chip,TDA8425_S1,s1); -} - - -/* ---------------------------------------------------------------------- */ -/* audio chip descriptions - defines+functions for pic16c54 (PV951) */ - -/* the registers of 16C54, I2C sub address. */ -#define PIC16C54_REG_KEY_CODE 0x01 /* Not use. */ -#define PIC16C54_REG_MISC 0x02 - -/* bit definition of the RESET register, I2C data. */ -#define PIC16C54_MISC_RESET_REMOTE_CTL 0x01 /* bit 0, Reset to receive the key */ - /* code of remote controller */ -#define PIC16C54_MISC_MTS_MAIN 0x02 /* bit 1 */ -#define PIC16C54_MISC_MTS_SAP 0x04 /* bit 2 */ -#define PIC16C54_MISC_MTS_BOTH 0x08 /* bit 3 */ -#define PIC16C54_MISC_SND_MUTE 0x10 /* bit 4, Mute Audio(Line-in and Tuner) */ -#define PIC16C54_MISC_SND_NOTMUTE 0x20 /* bit 5 */ -#define PIC16C54_MISC_SWITCH_TUNER 0x40 /* bit 6 , Switch to Line-in */ -#define PIC16C54_MISC_SWITCH_LINE 0x80 /* bit 7 , Switch to Tuner */ - -/* ---------------------------------------------------------------------- */ -/* audio chip descriptions - defines+functions for TA8874Z */ - -/* write 1st byte */ -#define TA8874Z_LED_STE 0x80 -#define TA8874Z_LED_BIL 0x40 -#define TA8874Z_LED_EXT 0x20 -#define TA8874Z_MONO_SET 0x10 -#define TA8874Z_MUTE 0x08 -#define TA8874Z_F_MONO 0x04 -#define TA8874Z_MODE_SUB 0x02 -#define TA8874Z_MODE_MAIN 0x01 - -/* write 2nd byte */ -/*#define TA8874Z_TI 0x80 */ /* test mode */ -#define TA8874Z_SEPARATION 0x3f -#define TA8874Z_SEPARATION_DEFAULT 0x10 - -/* read */ -#define TA8874Z_B1 0x80 -#define TA8874Z_B0 0x40 -#define TA8874Z_CHAG_FLAG 0x20 - -/* - * B1 B0 - * mono L H - * stereo L L - * BIL H L - */ -static int ta8874z_getrxsubchans(struct CHIPSTATE *chip) -{ - int val, mode; - - val = chip_read(chip); - mode = V4L2_TUNER_SUB_MONO; - if (val & TA8874Z_B1){ - mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - }else if (!(val & TA8874Z_B0)){ - mode = V4L2_TUNER_SUB_STEREO; - } - /* v4l2_dbg(1, debug, &chip->sd, - "ta8874z_getrxsubchans(): raw chip read: 0x%02x, return: 0x%02x\n", - val, mode); */ - return mode; -} - -static audiocmd ta8874z_stereo = { 2, {0, TA8874Z_SEPARATION_DEFAULT}}; -static audiocmd ta8874z_mono = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}}; -static audiocmd ta8874z_main = {2, { 0, TA8874Z_SEPARATION_DEFAULT}}; -static audiocmd ta8874z_sub = {2, { TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT}}; -static audiocmd ta8874z_both = {2, { TA8874Z_MODE_MAIN | TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT}}; - -static void ta8874z_setaudmode(struct CHIPSTATE *chip, int mode) -{ - struct v4l2_subdev *sd = &chip->sd; - int update = 1; - audiocmd *t = NULL; - - v4l2_dbg(1, debug, sd, "ta8874z_setaudmode(): mode: 0x%02x\n", mode); - - switch(mode){ - case V4L2_TUNER_MODE_MONO: - t = &ta8874z_mono; - break; - case V4L2_TUNER_MODE_STEREO: - t = &ta8874z_stereo; - break; - case V4L2_TUNER_MODE_LANG1: - t = &ta8874z_main; - break; - case V4L2_TUNER_MODE_LANG2: - t = &ta8874z_sub; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - t = &ta8874z_both; - break; - default: - update = 0; - } - - if(update) - chip_cmd(chip, "TA8874Z", t); -} - -static int ta8874z_checkit(struct CHIPSTATE *chip) -{ - int rc; - rc = chip_read(chip); - return ((rc & 0x1f) == 0x1f) ? 1 : 0; -} - -/* ---------------------------------------------------------------------- */ -/* audio chip descriptions - struct CHIPDESC */ - -/* insmod options to enable/disable individual audio chips */ -static int tda8425 = 1; -static int tda9840 = 1; -static int tda9850 = 1; -static int tda9855 = 1; -static int tda9873 = 1; -static int tda9874a = 1; -static int tda9875 = 1; -static int tea6300; /* default 0 - address clash with msp34xx */ -static int tea6320; /* default 0 - address clash with msp34xx */ -static int tea6420 = 1; -static int pic16c54 = 1; -static int ta8874z; /* default 0 - address clash with tda9840 */ - -module_param(tda8425, int, 0444); -module_param(tda9840, int, 0444); -module_param(tda9850, int, 0444); -module_param(tda9855, int, 0444); -module_param(tda9873, int, 0444); -module_param(tda9874a, int, 0444); -module_param(tda9875, int, 0444); -module_param(tea6300, int, 0444); -module_param(tea6320, int, 0444); -module_param(tea6420, int, 0444); -module_param(pic16c54, int, 0444); -module_param(ta8874z, int, 0444); - -static struct CHIPDESC chiplist[] = { - { - .name = "tda9840", - .insmodopt = &tda9840, - .addr_lo = I2C_ADDR_TDA9840 >> 1, - .addr_hi = I2C_ADDR_TDA9840 >> 1, - .registers = 5, - .flags = CHIP_NEED_CHECKMODE, - - /* callbacks */ - .checkit = tda9840_checkit, - .getrxsubchans = tda9840_getrxsubchans, - .setaudmode = tda9840_setaudmode, - - .init = { 2, { TDA9840_TEST, TDA9840_TEST_INT1SN - /* ,TDA9840_SW, TDA9840_MONO */} } - }, - { - .name = "tda9873h", - .insmodopt = &tda9873, - .addr_lo = I2C_ADDR_TDA985x_L >> 1, - .addr_hi = I2C_ADDR_TDA985x_H >> 1, - .registers = 3, - .flags = CHIP_HAS_INPUTSEL | CHIP_NEED_CHECKMODE, - - /* callbacks */ - .checkit = tda9873_checkit, - .getrxsubchans = tda9873_getrxsubchans, - .setaudmode = tda9873_setaudmode, - - .init = { 4, { TDA9873_SW, 0xa4, 0x06, 0x03 } }, - .inputreg = TDA9873_SW, - .inputmute = TDA9873_MUTE | TDA9873_AUTOMUTE, - .inputmap = {0xa0, 0xa2, 0xa0, 0xa0}, - .inputmask = TDA9873_INP_MASK|TDA9873_MUTE|TDA9873_AUTOMUTE, - - }, - { - .name = "tda9874h/a", - .insmodopt = &tda9874a, - .addr_lo = I2C_ADDR_TDA9874 >> 1, - .addr_hi = I2C_ADDR_TDA9874 >> 1, - .flags = CHIP_NEED_CHECKMODE, - - /* callbacks */ - .initialize = tda9874a_initialize, - .checkit = tda9874a_checkit, - .getrxsubchans = tda9874a_getrxsubchans, - .setaudmode = tda9874a_setaudmode, - }, - { - .name = "tda9875", - .insmodopt = &tda9875, - .addr_lo = I2C_ADDR_TDA9875 >> 1, - .addr_hi = I2C_ADDR_TDA9875 >> 1, - .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE, - - /* callbacks */ - .initialize = tda9875_initialize, - .checkit = tda9875_checkit, - .volfunc = tda9875_volume, - .bassfunc = tda9875_bass, - .treblefunc = tda9875_treble, - .leftreg = TDA9875_MVL, - .rightreg = TDA9875_MVR, - .bassreg = TDA9875_MBA, - .treblereg = TDA9875_MTR, - .leftinit = 58880, - .rightinit = 58880, - }, - { - .name = "tda9850", - .insmodopt = &tda9850, - .addr_lo = I2C_ADDR_TDA985x_L >> 1, - .addr_hi = I2C_ADDR_TDA985x_H >> 1, - .registers = 11, - - .getrxsubchans = tda985x_getrxsubchans, - .setaudmode = tda985x_setaudmode, - - .init = { 8, { TDA9850_C4, 0x08, 0x08, TDA985x_STEREO, 0x07, 0x10, 0x10, 0x03 } } - }, - { - .name = "tda9855", - .insmodopt = &tda9855, - .addr_lo = I2C_ADDR_TDA985x_L >> 1, - .addr_hi = I2C_ADDR_TDA985x_H >> 1, - .registers = 11, - .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE, - - .leftreg = TDA9855_VL, - .rightreg = TDA9855_VR, - .bassreg = TDA9855_BA, - .treblereg = TDA9855_TR, - - /* callbacks */ - .volfunc = tda9855_volume, - .bassfunc = tda9855_bass, - .treblefunc = tda9855_treble, - .getrxsubchans = tda985x_getrxsubchans, - .setaudmode = tda985x_setaudmode, - - .init = { 12, { 0, 0x6f, 0x6f, 0x0e, 0x07<<1, 0x8<<2, - TDA9855_MUTE | TDA9855_AVL | TDA9855_LOUD | TDA9855_INT, - TDA985x_STEREO | TDA9855_LINEAR | TDA9855_TZCM | TDA9855_VZCM, - 0x07, 0x10, 0x10, 0x03 }} - }, - { - .name = "tea6300", - .insmodopt = &tea6300, - .addr_lo = I2C_ADDR_TEA6300 >> 1, - .addr_hi = I2C_ADDR_TEA6300 >> 1, - .registers = 6, - .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL, - - .leftreg = TEA6300_VR, - .rightreg = TEA6300_VL, - .bassreg = TEA6300_BA, - .treblereg = TEA6300_TR, - - /* callbacks */ - .volfunc = tea6300_shift10, - .bassfunc = tea6300_shift12, - .treblefunc = tea6300_shift12, - - .inputreg = TEA6300_S, - .inputmap = { TEA6300_S_SA, TEA6300_S_SB, TEA6300_S_SC }, - .inputmute = TEA6300_S_GMU, - }, - { - .name = "tea6320", - .insmodopt = &tea6320, - .addr_lo = I2C_ADDR_TEA6300 >> 1, - .addr_hi = I2C_ADDR_TEA6300 >> 1, - .registers = 8, - .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL, - - .leftreg = TEA6320_V, - .rightreg = TEA6320_V, - .bassreg = TEA6320_BA, - .treblereg = TEA6320_TR, - - /* callbacks */ - .initialize = tea6320_initialize, - .volfunc = tea6320_volume, - .bassfunc = tea6320_shift11, - .treblefunc = tea6320_shift11, - - .inputreg = TEA6320_S, - .inputmap = { TEA6320_S_SA, TEA6420_S_SB, TEA6300_S_SC, TEA6320_S_SD }, - .inputmute = TEA6300_S_GMU, - }, - { - .name = "tea6420", - .insmodopt = &tea6420, - .addr_lo = I2C_ADDR_TEA6420 >> 1, - .addr_hi = I2C_ADDR_TEA6420 >> 1, - .registers = 1, - .flags = CHIP_HAS_INPUTSEL, - - .inputreg = -1, - .inputmap = { TEA6420_S_SA, TEA6420_S_SB, TEA6420_S_SC }, - .inputmute = TEA6300_S_GMU, - }, - { - .name = "tda8425", - .insmodopt = &tda8425, - .addr_lo = I2C_ADDR_TDA8425 >> 1, - .addr_hi = I2C_ADDR_TDA8425 >> 1, - .registers = 9, - .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL, - - .leftreg = TDA8425_VL, - .rightreg = TDA8425_VR, - .bassreg = TDA8425_BA, - .treblereg = TDA8425_TR, - - /* callbacks */ - .volfunc = tda8425_shift10, - .bassfunc = tda8425_shift12, - .treblefunc = tda8425_shift12, - .setaudmode = tda8425_setaudmode, - - .inputreg = TDA8425_S1, - .inputmap = { TDA8425_S1_CH1, TDA8425_S1_CH1, TDA8425_S1_CH1 }, - .inputmute = TDA8425_S1_OFF, - - }, - { - .name = "pic16c54 (PV951)", - .insmodopt = &pic16c54, - .addr_lo = I2C_ADDR_PIC16C54 >> 1, - .addr_hi = I2C_ADDR_PIC16C54>> 1, - .registers = 2, - .flags = CHIP_HAS_INPUTSEL, - - .inputreg = PIC16C54_REG_MISC, - .inputmap = {PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_TUNER, - PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_LINE, - PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_LINE, - PIC16C54_MISC_SND_MUTE}, - .inputmute = PIC16C54_MISC_SND_MUTE, - }, - { - .name = "ta8874z", - .checkit = ta8874z_checkit, - .insmodopt = &ta8874z, - .addr_lo = I2C_ADDR_TDA9840 >> 1, - .addr_hi = I2C_ADDR_TDA9840 >> 1, - .registers = 2, - - /* callbacks */ - .getrxsubchans = ta8874z_getrxsubchans, - .setaudmode = ta8874z_setaudmode, - - .init = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}}, - }, - { .name = NULL } /* EOF */ -}; - - -/* ---------------------------------------------------------------------- */ - -static int tvaudio_g_ctrl(struct v4l2_subdev *sd, - struct v4l2_control *ctrl) -{ - struct CHIPSTATE *chip = to_state(sd); - struct CHIPDESC *desc = chip->desc; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (!(desc->flags & CHIP_HAS_INPUTSEL)) - break; - ctrl->value=chip->muted; - return 0; - case V4L2_CID_AUDIO_VOLUME: - if (!(desc->flags & CHIP_HAS_VOLUME)) - break; - ctrl->value = max(chip->left,chip->right); - return 0; - case V4L2_CID_AUDIO_BALANCE: - { - int volume; - if (!(desc->flags & CHIP_HAS_VOLUME)) - break; - volume = max(chip->left,chip->right); - if (volume) - ctrl->value=(32768*min(chip->left,chip->right))/volume; - else - ctrl->value=32768; - return 0; - } - case V4L2_CID_AUDIO_BASS: - if (!(desc->flags & CHIP_HAS_BASSTREBLE)) - break; - ctrl->value = chip->bass; - return 0; - case V4L2_CID_AUDIO_TREBLE: - if (!(desc->flags & CHIP_HAS_BASSTREBLE)) - break; - ctrl->value = chip->treble; - return 0; - } - return -EINVAL; -} - -static int tvaudio_s_ctrl(struct v4l2_subdev *sd, - struct v4l2_control *ctrl) -{ - struct CHIPSTATE *chip = to_state(sd); - struct CHIPDESC *desc = chip->desc; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (!(desc->flags & CHIP_HAS_INPUTSEL)) - break; - - if (ctrl->value < 0 || ctrl->value >= 2) - return -ERANGE; - chip->muted = ctrl->value; - if (chip->muted) - chip_write_masked(chip,desc->inputreg,desc->inputmute,desc->inputmask); - else - chip_write_masked(chip,desc->inputreg, - desc->inputmap[chip->input],desc->inputmask); - return 0; - case V4L2_CID_AUDIO_VOLUME: - { - int volume,balance; - - if (!(desc->flags & CHIP_HAS_VOLUME)) - break; - - volume = max(chip->left,chip->right); - if (volume) - balance=(32768*min(chip->left,chip->right))/volume; - else - balance=32768; - - volume=ctrl->value; - chip->left = (min(65536 - balance,32768) * volume) / 32768; - chip->right = (min(balance,volume *(__u16)32768)) / 32768; - - chip_write(chip,desc->leftreg,desc->volfunc(chip->left)); - chip_write(chip,desc->rightreg,desc->volfunc(chip->right)); - - return 0; - } - case V4L2_CID_AUDIO_BALANCE: - { - int volume, balance; - - if (!(desc->flags & CHIP_HAS_VOLUME)) - break; - - volume = max(chip->left, chip->right); - balance = ctrl->value; - chip->left = (min(65536 - balance, 32768) * volume) / 32768; - chip->right = (min(balance, volume * (__u16)32768)) / 32768; - - chip_write(chip, desc->leftreg, desc->volfunc(chip->left)); - chip_write(chip, desc->rightreg, desc->volfunc(chip->right)); - - return 0; - } - case V4L2_CID_AUDIO_BASS: - if (!(desc->flags & CHIP_HAS_BASSTREBLE)) - break; - chip->bass = ctrl->value; - chip_write(chip,desc->bassreg,desc->bassfunc(chip->bass)); - - return 0; - case V4L2_CID_AUDIO_TREBLE: - if (!(desc->flags & CHIP_HAS_BASSTREBLE)) - break; - chip->treble = ctrl->value; - chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble)); - - return 0; - } - return -EINVAL; -} - - -/* ---------------------------------------------------------------------- */ -/* video4linux interface */ - -static int tvaudio_s_radio(struct v4l2_subdev *sd) -{ - struct CHIPSTATE *chip = to_state(sd); - - chip->radio = 1; - /* del_timer(&chip->wt); */ - return 0; -} - -static int tvaudio_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - struct CHIPSTATE *chip = to_state(sd); - struct CHIPDESC *desc = chip->desc; - - switch (qc->id) { - case V4L2_CID_AUDIO_MUTE: - if (desc->flags & CHIP_HAS_INPUTSEL) - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); - break; - case V4L2_CID_AUDIO_VOLUME: - if (desc->flags & CHIP_HAS_VOLUME) - return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880); - break; - case V4L2_CID_AUDIO_BALANCE: - if (desc->flags & CHIP_HAS_VOLUME) - return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); - break; - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - if (desc->flags & CHIP_HAS_BASSTREBLE) - return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); - break; - default: - break; - } - return -EINVAL; -} - -static int tvaudio_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct CHIPSTATE *chip = to_state(sd); - struct CHIPDESC *desc = chip->desc; - - if (!(desc->flags & CHIP_HAS_INPUTSEL)) - return 0; - if (input >= 4) - return -EINVAL; - /* There are four inputs: tuner, radio, extern and intern. */ - chip->input = input; - if (chip->muted) - return 0; - chip_write_masked(chip, desc->inputreg, - desc->inputmap[chip->input], desc->inputmask); - return 0; -} - -static int tvaudio_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct CHIPSTATE *chip = to_state(sd); - struct CHIPDESC *desc = chip->desc; - - if (!desc->setaudmode) - return 0; - if (chip->radio) - return 0; - - switch (vt->audmode) { - case V4L2_TUNER_MODE_MONO: - case V4L2_TUNER_MODE_STEREO: - case V4L2_TUNER_MODE_LANG1: - case V4L2_TUNER_MODE_LANG2: - case V4L2_TUNER_MODE_LANG1_LANG2: - break; - default: - return -EINVAL; - } - chip->audmode = vt->audmode; - - if (chip->thread) - wake_up_process(chip->thread); - else - desc->setaudmode(chip, vt->audmode); - - return 0; -} - -static int tvaudio_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct CHIPSTATE *chip = to_state(sd); - struct CHIPDESC *desc = chip->desc; - - if (!desc->getrxsubchans) - return 0; - if (chip->radio) - return 0; - - vt->audmode = chip->audmode; - vt->rxsubchans = desc->getrxsubchans(chip); - vt->capability = V4L2_TUNER_CAP_STEREO | - V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; - - return 0; -} - -static int tvaudio_s_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct CHIPSTATE *chip = to_state(sd); - - chip->radio = 0; - return 0; -} - -static int tvaudio_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) -{ - struct CHIPSTATE *chip = to_state(sd); - struct CHIPDESC *desc = chip->desc; - - /* For chips that provide getrxsubchans and setaudmode, and doesn't - automatically follows the stereo carrier, a kthread is - created to set the audio standard. In this case, when then - the video channel is changed, tvaudio starts on MONO mode. - After waiting for 2 seconds, the kernel thread is called, - to follow whatever audio standard is pointed by the - audio carrier. - */ - if (chip->thread) { - desc->setaudmode(chip, V4L2_TUNER_MODE_MONO); - chip->prevmode = -1; /* reset previous mode */ - mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000)); - } - return 0; -} - -static int tvaudio_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVAUDIO, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops tvaudio_core_ops = { - .g_chip_ident = tvaudio_g_chip_ident, - .queryctrl = tvaudio_queryctrl, - .g_ctrl = tvaudio_g_ctrl, - .s_ctrl = tvaudio_s_ctrl, - .s_std = tvaudio_s_std, -}; - -static const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = { - .s_radio = tvaudio_s_radio, - .s_frequency = tvaudio_s_frequency, - .s_tuner = tvaudio_s_tuner, - .g_tuner = tvaudio_g_tuner, -}; - -static const struct v4l2_subdev_audio_ops tvaudio_audio_ops = { - .s_routing = tvaudio_s_routing, -}; - -static const struct v4l2_subdev_ops tvaudio_ops = { - .core = &tvaudio_core_ops, - .tuner = &tvaudio_tuner_ops, - .audio = &tvaudio_audio_ops, -}; - -/* ----------------------------------------------------------------------- */ - - -/* i2c registration */ - -static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ - struct CHIPSTATE *chip; - struct CHIPDESC *desc; - struct v4l2_subdev *sd; - - if (debug) { - printk(KERN_INFO "tvaudio: TV audio decoder + audio/video mux driver\n"); - printk(KERN_INFO "tvaudio: known chips: "); - for (desc = chiplist; desc->name != NULL; desc++) - printk("%s%s", (desc == chiplist) ? "" : ", ", desc->name); - printk("\n"); - } - - chip = kzalloc(sizeof(*chip), GFP_KERNEL); - if (!chip) - return -ENOMEM; - sd = &chip->sd; - v4l2_i2c_subdev_init(sd, client, &tvaudio_ops); - - /* find description for the chip */ - v4l2_dbg(1, debug, sd, "chip found @ 0x%x\n", client->addr<<1); - for (desc = chiplist; desc->name != NULL; desc++) { - if (0 == *(desc->insmodopt)) - continue; - if (client->addr < desc->addr_lo || - client->addr > desc->addr_hi) - continue; - if (desc->checkit && !desc->checkit(chip)) - continue; - break; - } - if (desc->name == NULL) { - v4l2_dbg(1, debug, sd, "no matching chip description found\n"); - kfree(chip); - return -EIO; - } - v4l2_info(sd, "%s found @ 0x%x (%s)\n", desc->name, client->addr<<1, client->adapter->name); - if (desc->flags) { - v4l2_dbg(1, debug, sd, "matches:%s%s%s.\n", - (desc->flags & CHIP_HAS_VOLUME) ? " volume" : "", - (desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "", - (desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : ""); - } - - /* fill required data structures */ - if (!id) - strlcpy(client->name, desc->name, I2C_NAME_SIZE); - chip->desc = desc; - chip->shadow.count = desc->registers+1; - chip->prevmode = -1; - chip->audmode = V4L2_TUNER_MODE_LANG1; - - /* initialization */ - if (desc->initialize != NULL) - desc->initialize(chip); - else - chip_cmd(chip, "init", &desc->init); - - if (desc->flags & CHIP_HAS_VOLUME) { - if (!desc->volfunc) { - /* This shouldn't be happen. Warn user, but keep working - without volume controls - */ - v4l2_info(sd, "volume callback undefined!\n"); - desc->flags &= ~CHIP_HAS_VOLUME; - } else { - chip->left = desc->leftinit ? desc->leftinit : 65535; - chip->right = desc->rightinit ? desc->rightinit : 65535; - chip_write(chip, desc->leftreg, - desc->volfunc(chip->left)); - chip_write(chip, desc->rightreg, - desc->volfunc(chip->right)); - } - } - if (desc->flags & CHIP_HAS_BASSTREBLE) { - if (!desc->bassfunc || !desc->treblefunc) { - /* This shouldn't be happen. Warn user, but keep working - without bass/treble controls - */ - v4l2_info(sd, "bass/treble callbacks undefined!\n"); - desc->flags &= ~CHIP_HAS_BASSTREBLE; - } else { - chip->treble = desc->trebleinit ? - desc->trebleinit : 32768; - chip->bass = desc->bassinit ? - desc->bassinit : 32768; - chip_write(chip, desc->bassreg, - desc->bassfunc(chip->bass)); - chip_write(chip, desc->treblereg, - desc->treblefunc(chip->treble)); - } - } - - chip->thread = NULL; - init_timer(&chip->wt); - if (desc->flags & CHIP_NEED_CHECKMODE) { - if (!desc->getrxsubchans || !desc->setaudmode) { - /* This shouldn't be happen. Warn user, but keep working - without kthread - */ - v4l2_info(sd, "set/get mode callbacks undefined!\n"); - return 0; - } - /* start async thread */ - chip->wt.function = chip_thread_wake; - chip->wt.data = (unsigned long)chip; - chip->thread = kthread_run(chip_thread, chip, client->name); - if (IS_ERR(chip->thread)) { - v4l2_warn(sd, "failed to create kthread\n"); - chip->thread = NULL; - } - } - return 0; -} - -static int tvaudio_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct CHIPSTATE *chip = to_state(sd); - - del_timer_sync(&chip->wt); - if (chip->thread) { - /* shutdown async thread */ - kthread_stop(chip->thread); - chip->thread = NULL; - } - - v4l2_device_unregister_subdev(sd); - kfree(chip); - return 0; -} - -/* This driver supports many devices and the idea is to let the driver - detect which device is present. So rather than listing all supported - devices here, we pretend to support a single, fake device type. */ -static const struct i2c_device_id tvaudio_id[] = { - { "tvaudio", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tvaudio_id); - -static struct i2c_driver tvaudio_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "tvaudio", - }, - .probe = tvaudio_probe, - .remove = tvaudio_remove, - .id_table = tvaudio_id, -}; - -module_i2c_driver(tvaudio_driver); diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c deleted file mode 100644 index 3b6cf034976..00000000000 --- a/drivers/media/video/tveeprom.c +++ /dev/null @@ -1,792 +0,0 @@ -/* - * tveeprom - eeprom decoder for tvcard configuration eeproms - * - * Data and decoding routines shamelessly borrowed from bttv-cards.c - * eeprom access routine shamelessly borrowed from bttv-if.c - * which are: - - Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) - & Marcus Metzler (mocm@thp.uni-koeln.de) - (c) 1999-2001 Gerd Knorr - - * Adjustments to fit a more general model and all bugs: - - Copyright (C) 2003 John Klar - - * 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 -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -MODULE_DESCRIPTION("i2c Hauppauge eeprom decoder driver"); -MODULE_AUTHOR("John Klar"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - -#define STRM(array, i) \ - (i < sizeof(array) / sizeof(char *) ? array[i] : "unknown") - -#define tveeprom_info(fmt, arg...) \ - v4l_printk(KERN_INFO, "tveeprom", c->adapter, c->addr, fmt , ## arg) -#define tveeprom_warn(fmt, arg...) \ - v4l_printk(KERN_WARNING, "tveeprom", c->adapter, c->addr, fmt , ## arg) -#define tveeprom_dbg(fmt, arg...) do { \ - if (debug) \ - v4l_printk(KERN_DEBUG, "tveeprom", \ - c->adapter, c->addr, fmt , ## arg); \ - } while (0) - -/* - * The Hauppauge eeprom uses an 8bit field to determine which - * tuner formats the tuner supports. - */ -static struct HAUPPAUGE_TUNER_FMT -{ - int id; - char *name; -} -hauppauge_tuner_fmt[] = -{ - { V4L2_STD_UNKNOWN, " UNKNOWN" }, - { V4L2_STD_UNKNOWN, " FM" }, - { V4L2_STD_B|V4L2_STD_GH, " PAL(B/G)" }, - { V4L2_STD_MN, " NTSC(M)" }, - { V4L2_STD_PAL_I, " PAL(I)" }, - { V4L2_STD_SECAM_L|V4L2_STD_SECAM_LC, " SECAM(L/L')" }, - { V4L2_STD_DK, " PAL(D/D1/K)" }, - { V4L2_STD_ATSC, " ATSC/DVB Digital" }, -}; - -/* This is the full list of possible tuners. Many thanks to Hauppauge for - supplying this information. Note that many tuners where only used for - testing and never made it to the outside world. So you will only see - a subset in actual produced cards. */ -static struct HAUPPAUGE_TUNER -{ - int id; - char *name; -} -hauppauge_tuner[] = -{ - /* 0-9 */ - { TUNER_ABSENT, "None" }, - { TUNER_ABSENT, "External" }, - { TUNER_ABSENT, "Unspecified" }, - { TUNER_PHILIPS_PAL, "Philips FI1216" }, - { TUNER_PHILIPS_SECAM, "Philips FI1216MF" }, - { TUNER_PHILIPS_NTSC, "Philips FI1236" }, - { TUNER_PHILIPS_PAL_I, "Philips FI1246" }, - { TUNER_PHILIPS_PAL_DK, "Philips FI1256" }, - { TUNER_PHILIPS_PAL, "Philips FI1216 MK2" }, - { TUNER_PHILIPS_SECAM, "Philips FI1216MF MK2" }, - /* 10-19 */ - { TUNER_PHILIPS_NTSC, "Philips FI1236 MK2" }, - { TUNER_PHILIPS_PAL_I, "Philips FI1246 MK2" }, - { TUNER_PHILIPS_PAL_DK, "Philips FI1256 MK2" }, - { TUNER_TEMIC_NTSC, "Temic 4032FY5" }, - { TUNER_TEMIC_PAL, "Temic 4002FH5" }, - { TUNER_TEMIC_PAL_I, "Temic 4062FY5" }, - { TUNER_PHILIPS_PAL, "Philips FR1216 MK2" }, - { TUNER_PHILIPS_SECAM, "Philips FR1216MF MK2" }, - { TUNER_PHILIPS_NTSC, "Philips FR1236 MK2" }, - { TUNER_PHILIPS_PAL_I, "Philips FR1246 MK2" }, - /* 20-29 */ - { TUNER_PHILIPS_PAL_DK, "Philips FR1256 MK2" }, - { TUNER_PHILIPS_PAL, "Philips FM1216" }, - { TUNER_PHILIPS_SECAM, "Philips FM1216MF" }, - { TUNER_PHILIPS_NTSC, "Philips FM1236" }, - { TUNER_PHILIPS_PAL_I, "Philips FM1246" }, - { TUNER_PHILIPS_PAL_DK, "Philips FM1256" }, - { TUNER_TEMIC_4036FY5_NTSC, "Temic 4036FY5" }, - { TUNER_ABSENT, "Samsung TCPN9082D" }, - { TUNER_ABSENT, "Samsung TCPM9092P" }, - { TUNER_TEMIC_4006FH5_PAL, "Temic 4006FH5" }, - /* 30-39 */ - { TUNER_ABSENT, "Samsung TCPN9085D" }, - { TUNER_ABSENT, "Samsung TCPB9085P" }, - { TUNER_ABSENT, "Samsung TCPL9091P" }, - { TUNER_TEMIC_4039FR5_NTSC, "Temic 4039FR5" }, - { TUNER_PHILIPS_FQ1216ME, "Philips FQ1216 ME" }, - { TUNER_TEMIC_4066FY5_PAL_I, "Temic 4066FY5" }, - { TUNER_PHILIPS_NTSC, "Philips TD1536" }, - { TUNER_PHILIPS_NTSC, "Philips TD1536D" }, - { TUNER_PHILIPS_NTSC, "Philips FMR1236" }, /* mono radio */ - { TUNER_ABSENT, "Philips FI1256MP" }, - /* 40-49 */ - { TUNER_ABSENT, "Samsung TCPQ9091P" }, - { TUNER_TEMIC_4006FN5_MULTI_PAL, "Temic 4006FN5" }, - { TUNER_TEMIC_4009FR5_PAL, "Temic 4009FR5" }, - { TUNER_TEMIC_4046FM5, "Temic 4046FM5" }, - { TUNER_TEMIC_4009FN5_MULTI_PAL_FM, "Temic 4009FN5" }, - { TUNER_ABSENT, "Philips TD1536D FH 44"}, - { TUNER_LG_NTSC_FM, "LG TP18NSR01F"}, - { TUNER_LG_PAL_FM, "LG TP18PSB01D"}, - { TUNER_LG_PAL, "LG TP18PSB11D"}, - { TUNER_LG_PAL_I_FM, "LG TAPC-I001D"}, - /* 50-59 */ - { TUNER_LG_PAL_I, "LG TAPC-I701D"}, - { TUNER_ABSENT, "Temic 4042FI5"}, - { TUNER_MICROTUNE_4049FM5, "Microtune 4049 FM5"}, - { TUNER_ABSENT, "LG TPI8NSR11F"}, - { TUNER_ABSENT, "Microtune 4049 FM5 Alt I2C"}, - { TUNER_PHILIPS_FM1216ME_MK3, "Philips FQ1216ME MK3"}, - { TUNER_ABSENT, "Philips FI1236 MK3"}, - { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216 ME MK3"}, - { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK3"}, - { TUNER_ABSENT, "Philips FM1216MP MK3"}, - /* 60-69 */ - { TUNER_PHILIPS_FM1216ME_MK3, "LG S001D MK3"}, - { TUNER_ABSENT, "LG M001D MK3"}, - { TUNER_PHILIPS_FM1216ME_MK3, "LG S701D MK3"}, - { TUNER_ABSENT, "LG M701D MK3"}, - { TUNER_ABSENT, "Temic 4146FM5"}, - { TUNER_ABSENT, "Temic 4136FY5"}, - { TUNER_ABSENT, "Temic 4106FH5"}, - { TUNER_ABSENT, "Philips FQ1216LMP MK3"}, - { TUNER_LG_NTSC_TAPE, "LG TAPE H001F MK3"}, - { TUNER_LG_NTSC_TAPE, "LG TAPE H701F MK3"}, - /* 70-79 */ - { TUNER_ABSENT, "LG TALN H200T"}, - { TUNER_ABSENT, "LG TALN H250T"}, - { TUNER_ABSENT, "LG TALN M200T"}, - { TUNER_ABSENT, "LG TALN Z200T"}, - { TUNER_ABSENT, "LG TALN S200T"}, - { TUNER_ABSENT, "Thompson DTT7595"}, - { TUNER_ABSENT, "Thompson DTT7592"}, - { TUNER_ABSENT, "Silicon TDA8275C1 8290"}, - { TUNER_ABSENT, "Silicon TDA8275C1 8290 FM"}, - { TUNER_ABSENT, "Thompson DTT757"}, - /* 80-89 */ - { TUNER_PHILIPS_FQ1216LME_MK3, "Philips FQ1216LME MK3"}, - { TUNER_LG_PAL_NEW_TAPC, "LG TAPC G701D"}, - { TUNER_LG_NTSC_NEW_TAPC, "LG TAPC H791F"}, - { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MB 3"}, - { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MI 3"}, - { TUNER_TCL_2002N, "TCL 2002N 6A"}, - { TUNER_PHILIPS_FM1236_MK3, "Philips FQ1236 MK3"}, - { TUNER_SAMSUNG_TCPN_2121P30A, "Samsung TCPN 2121P30A"}, - { TUNER_ABSENT, "Samsung TCPE 4121P30A"}, - { TUNER_PHILIPS_FM1216ME_MK3, "TCL MFPE05 2"}, - /* 90-99 */ - { TUNER_ABSENT, "LG TALN H202T"}, - { TUNER_PHILIPS_FQ1216AME_MK4, "Philips FQ1216AME MK4"}, - { TUNER_PHILIPS_FQ1236A_MK4, "Philips FQ1236A MK4"}, - { TUNER_ABSENT, "Philips FQ1286A MK4"}, - { TUNER_ABSENT, "Philips FQ1216ME MK5"}, - { TUNER_ABSENT, "Philips FQ1236 MK5"}, - { TUNER_SAMSUNG_TCPG_6121P30A, "Samsung TCPG 6121P30A"}, - { TUNER_TCL_2002MB, "TCL 2002MB_3H"}, - { TUNER_ABSENT, "TCL 2002MI_3H"}, - { TUNER_TCL_2002N, "TCL 2002N 5H"}, - /* 100-109 */ - { TUNER_PHILIPS_FMD1216ME_MK3, "Philips FMD1216ME"}, - { TUNER_TEA5767, "Philips TEA5768HL FM Radio"}, - { TUNER_ABSENT, "Panasonic ENV57H12D5"}, - { TUNER_PHILIPS_FM1236_MK3, "TCL MFNM05-4"}, - { TUNER_PHILIPS_FM1236_MK3, "TCL MNM05-4"}, - { TUNER_PHILIPS_FM1216ME_MK3, "TCL MPE05-2"}, - { TUNER_ABSENT, "TCL MQNM05-4"}, - { TUNER_ABSENT, "LG TAPC-W701D"}, - { TUNER_ABSENT, "TCL 9886P-WM"}, - { TUNER_ABSENT, "TCL 1676NM-WM"}, - /* 110-119 */ - { TUNER_ABSENT, "Thompson DTT75105"}, - { TUNER_ABSENT, "Conexant_CX24109"}, - { TUNER_TCL_2002N, "TCL M2523_5N_E"}, - { TUNER_TCL_2002MB, "TCL M2523_3DB_E"}, - { TUNER_ABSENT, "Philips 8275A"}, - { TUNER_ABSENT, "Microtune MT2060"}, - { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK5"}, - { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216ME MK5"}, - { TUNER_ABSENT, "TCL M2523_3DI_E"}, - { TUNER_ABSENT, "Samsung THPD5222FG30A"}, - /* 120-129 */ - { TUNER_XC2028, "Xceive XC3028"}, - { TUNER_PHILIPS_FQ1216LME_MK3, "Philips FQ1216LME MK5"}, - { TUNER_ABSENT, "Philips FQD1216LME"}, - { TUNER_ABSENT, "Conexant CX24118A"}, - { TUNER_ABSENT, "TCL DMF11WIP"}, - { TUNER_ABSENT, "TCL MFNM05_4H_E"}, - { TUNER_ABSENT, "TCL MNM05_4H_E"}, - { TUNER_ABSENT, "TCL MPE05_2H_E"}, - { TUNER_ABSENT, "TCL MQNM05_4_U"}, - { TUNER_ABSENT, "TCL M2523_5NH_E"}, - /* 130-139 */ - { TUNER_ABSENT, "TCL M2523_3DBH_E"}, - { TUNER_ABSENT, "TCL M2523_3DIH_E"}, - { TUNER_ABSENT, "TCL MFPE05_2_U"}, - { TUNER_PHILIPS_FMD1216MEX_MK3, "Philips FMD1216MEX"}, - { TUNER_ABSENT, "Philips FRH2036B"}, - { TUNER_ABSENT, "Panasonic ENGF75_01GF"}, - { TUNER_ABSENT, "MaxLinear MXL5005"}, - { TUNER_ABSENT, "MaxLinear MXL5003"}, - { TUNER_ABSENT, "Xceive XC2028"}, - { TUNER_ABSENT, "Microtune MT2131"}, - /* 140-149 */ - { TUNER_ABSENT, "Philips 8275A_8295"}, - { TUNER_ABSENT, "TCL MF02GIP_5N_E"}, - { TUNER_ABSENT, "TCL MF02GIP_3DB_E"}, - { TUNER_ABSENT, "TCL MF02GIP_3DI_E"}, - { TUNER_ABSENT, "Microtune MT2266"}, - { TUNER_ABSENT, "TCL MF10WPP_4N_E"}, - { TUNER_ABSENT, "LG TAPQ_H702F"}, - { TUNER_ABSENT, "TCL M09WPP_4N_E"}, - { TUNER_ABSENT, "MaxLinear MXL5005_v2"}, - { TUNER_PHILIPS_TDA8290, "Philips 18271_8295"}, - /* 150-159 */ - { TUNER_XC5000, "Xceive XC5000"}, - { TUNER_ABSENT, "Xceive XC3028L"}, - { TUNER_ABSENT, "NXP 18271C2_716x"}, - { TUNER_ABSENT, "Xceive XC4000"}, - { TUNER_ABSENT, "Dibcom 7070"}, - { TUNER_PHILIPS_TDA8290, "NXP 18271C2"}, - { TUNER_ABSENT, "Siano SMS1010"}, - { TUNER_ABSENT, "Siano SMS1150"}, - { TUNER_ABSENT, "MaxLinear 5007"}, - { TUNER_ABSENT, "TCL M09WPP_2P_E"}, - /* 160-169 */ - { TUNER_ABSENT, "Siano SMS1180"}, - { TUNER_ABSENT, "Maxim_MAX2165"}, - { TUNER_ABSENT, "Siano SMS1140"}, - { TUNER_ABSENT, "Siano SMS1150 B1"}, - { TUNER_ABSENT, "MaxLinear 111"}, - { TUNER_ABSENT, "Dibcom 7770"}, - { TUNER_ABSENT, "Siano SMS1180VNS"}, - { TUNER_ABSENT, "Siano SMS1184"}, - { TUNER_PHILIPS_FQ1236_MK5, "TCL M30WTP-4N-E"}, - { TUNER_ABSENT, "TCL_M11WPP_2PN_E"}, - /* 170-179 */ - { TUNER_ABSENT, "MaxLinear 301"}, - { TUNER_ABSENT, "Mirics MSi001"}, - { TUNER_ABSENT, "MaxLinear MxL241SF"}, - { TUNER_XC5000C, "Xceive XC5000C"}, - { TUNER_ABSENT, "Montage M68TS2020"}, - { TUNER_ABSENT, "Siano SMS1530"}, - { TUNER_ABSENT, "Dibcom 7090"}, - { TUNER_ABSENT, "Xceive XC5200C"}, - { TUNER_ABSENT, "NXP 18273"}, - { TUNER_ABSENT, "Montage M88TS2022"}, - /* 180-189 */ - { TUNER_ABSENT, "NXP 18272M"}, - { TUNER_ABSENT, "NXP 18272S"}, -}; - -/* Use V4L2_IDENT_AMBIGUOUS for those audio 'chips' that are - * internal to a video chip, i.e. not a separate audio chip. */ -static struct HAUPPAUGE_AUDIOIC -{ - u32 id; - char *name; -} -audioIC[] = -{ - /* 0-4 */ - { V4L2_IDENT_NONE, "None" }, - { V4L2_IDENT_UNKNOWN, "TEA6300" }, - { V4L2_IDENT_UNKNOWN, "TEA6320" }, - { V4L2_IDENT_UNKNOWN, "TDA9850" }, - { V4L2_IDENT_MSPX4XX, "MSP3400C" }, - /* 5-9 */ - { V4L2_IDENT_MSPX4XX, "MSP3410D" }, - { V4L2_IDENT_MSPX4XX, "MSP3415" }, - { V4L2_IDENT_MSPX4XX, "MSP3430" }, - { V4L2_IDENT_MSPX4XX, "MSP3438" }, - { V4L2_IDENT_UNKNOWN, "CS5331" }, - /* 10-14 */ - { V4L2_IDENT_MSPX4XX, "MSP3435" }, - { V4L2_IDENT_MSPX4XX, "MSP3440" }, - { V4L2_IDENT_MSPX4XX, "MSP3445" }, - { V4L2_IDENT_MSPX4XX, "MSP3411" }, - { V4L2_IDENT_MSPX4XX, "MSP3416" }, - /* 15-19 */ - { V4L2_IDENT_MSPX4XX, "MSP3425" }, - { V4L2_IDENT_MSPX4XX, "MSP3451" }, - { V4L2_IDENT_MSPX4XX, "MSP3418" }, - { V4L2_IDENT_UNKNOWN, "Type 0x12" }, - { V4L2_IDENT_UNKNOWN, "OKI7716" }, - /* 20-24 */ - { V4L2_IDENT_MSPX4XX, "MSP4410" }, - { V4L2_IDENT_MSPX4XX, "MSP4420" }, - { V4L2_IDENT_MSPX4XX, "MSP4440" }, - { V4L2_IDENT_MSPX4XX, "MSP4450" }, - { V4L2_IDENT_MSPX4XX, "MSP4408" }, - /* 25-29 */ - { V4L2_IDENT_MSPX4XX, "MSP4418" }, - { V4L2_IDENT_MSPX4XX, "MSP4428" }, - { V4L2_IDENT_MSPX4XX, "MSP4448" }, - { V4L2_IDENT_MSPX4XX, "MSP4458" }, - { V4L2_IDENT_MSPX4XX, "Type 0x1d" }, - /* 30-34 */ - { V4L2_IDENT_AMBIGUOUS, "CX880" }, - { V4L2_IDENT_AMBIGUOUS, "CX881" }, - { V4L2_IDENT_AMBIGUOUS, "CX883" }, - { V4L2_IDENT_AMBIGUOUS, "CX882" }, - { V4L2_IDENT_AMBIGUOUS, "CX25840" }, - /* 35-39 */ - { V4L2_IDENT_AMBIGUOUS, "CX25841" }, - { V4L2_IDENT_AMBIGUOUS, "CX25842" }, - { V4L2_IDENT_AMBIGUOUS, "CX25843" }, - { V4L2_IDENT_AMBIGUOUS, "CX23418" }, - { V4L2_IDENT_AMBIGUOUS, "CX23885" }, - /* 40-44 */ - { V4L2_IDENT_AMBIGUOUS, "CX23888" }, - { V4L2_IDENT_AMBIGUOUS, "SAA7131" }, - { V4L2_IDENT_AMBIGUOUS, "CX23887" }, - { V4L2_IDENT_AMBIGUOUS, "SAA7164" }, - { V4L2_IDENT_AMBIGUOUS, "AU8522" }, -}; - -/* This list is supplied by Hauppauge. Thanks! */ -static const char *decoderIC[] = { - /* 0-4 */ - "None", "BT815", "BT817", "BT819", "BT815A", - /* 5-9 */ - "BT817A", "BT819A", "BT827", "BT829", "BT848", - /* 10-14 */ - "BT848A", "BT849A", "BT829A", "BT827A", "BT878", - /* 15-19 */ - "BT879", "BT880", "VPX3226E", "SAA7114", "SAA7115", - /* 20-24 */ - "CX880", "CX881", "CX883", "SAA7111", "SAA7113", - /* 25-29 */ - "CX882", "TVP5150A", "CX25840", "CX25841", "CX25842", - /* 30-34 */ - "CX25843", "CX23418", "NEC61153", "CX23885", "CX23888", - /* 35-39 */ - "SAA7131", "CX25837", "CX23887", "CX23885A", "CX23887A", - /* 40-42 */ - "SAA7164", "CX23885B", "AU8522" -}; - -static int hasRadioTuner(int tunerType) -{ - switch (tunerType) { - case 18: /* PNPEnv_TUNER_FR1236_MK2 */ - case 23: /* PNPEnv_TUNER_FM1236 */ - case 38: /* PNPEnv_TUNER_FMR1236 */ - case 16: /* PNPEnv_TUNER_FR1216_MK2 */ - case 19: /* PNPEnv_TUNER_FR1246_MK2 */ - case 21: /* PNPEnv_TUNER_FM1216 */ - case 24: /* PNPEnv_TUNER_FM1246 */ - case 17: /* PNPEnv_TUNER_FR1216MF_MK2 */ - case 22: /* PNPEnv_TUNER_FM1216MF */ - case 20: /* PNPEnv_TUNER_FR1256_MK2 */ - case 25: /* PNPEnv_TUNER_FM1256 */ - case 33: /* PNPEnv_TUNER_4039FR5 */ - case 42: /* PNPEnv_TUNER_4009FR5 */ - case 52: /* PNPEnv_TUNER_4049FM5 */ - case 54: /* PNPEnv_TUNER_4049FM5_AltI2C */ - case 44: /* PNPEnv_TUNER_4009FN5 */ - case 31: /* PNPEnv_TUNER_TCPB9085P */ - case 30: /* PNPEnv_TUNER_TCPN9085D */ - case 46: /* PNPEnv_TUNER_TP18NSR01F */ - case 47: /* PNPEnv_TUNER_TP18PSB01D */ - case 49: /* PNPEnv_TUNER_TAPC_I001D */ - case 60: /* PNPEnv_TUNER_TAPE_S001D_MK3 */ - case 57: /* PNPEnv_TUNER_FM1216ME_MK3 */ - case 59: /* PNPEnv_TUNER_FM1216MP_MK3 */ - case 58: /* PNPEnv_TUNER_FM1236_MK3 */ - case 68: /* PNPEnv_TUNER_TAPE_H001F_MK3 */ - case 61: /* PNPEnv_TUNER_TAPE_M001D_MK3 */ - case 78: /* PNPEnv_TUNER_TDA8275C1_8290_FM */ - case 89: /* PNPEnv_TUNER_TCL_MFPE05_2 */ - case 92: /* PNPEnv_TUNER_PHILIPS_FQ1236A_MK4 */ - case 105: - return 1; - } - return 0; -} - -void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, - unsigned char *eeprom_data) -{ - /* ---------------------------------------------- - ** The hauppauge eeprom format is tagged - ** - ** if packet[0] == 0x84, then packet[0..1] == length - ** else length = packet[0] & 3f; - ** if packet[0] & f8 == f8, then EOD and packet[1] == checksum - ** - ** In our (ivtv) case we're interested in the following: - ** tuner type: tag [00].05 or [0a].01 (index into hauppauge_tuner) - ** tuner fmts: tag [00].04 or [0a].00 (bitmask index into - ** hauppauge_tuner_fmt) - ** radio: tag [00].{last} or [0e].00 (bitmask. bit2=FM) - ** audio proc: tag [02].01 or [05].00 (mask with 0x7f) - ** decoder proc: tag [09].01) - - ** Fun info: - ** model: tag [00].07-08 or [06].00-01 - ** revision: tag [00].09-0b or [06].04-06 - ** serial#: tag [01].05-07 or [04].04-06 - - ** # of inputs/outputs ??? - */ - - int i, j, len, done, beenhere, tag, start; - - int tuner1 = 0, t_format1 = 0, audioic = -1; - char *t_name1 = NULL; - const char *t_fmt_name1[8] = { " none", "", "", "", "", "", "", "" }; - - int tuner2 = 0, t_format2 = 0; - char *t_name2 = NULL; - const char *t_fmt_name2[8] = { " none", "", "", "", "", "", "", "" }; - - memset(tvee, 0, sizeof(*tvee)); - tvee->tuner_type = TUNER_ABSENT; - tvee->tuner2_type = TUNER_ABSENT; - - done = len = beenhere = 0; - - /* Different eeprom start offsets for em28xx, cx2388x and cx23418 */ - if (eeprom_data[0] == 0x1a && - eeprom_data[1] == 0xeb && - eeprom_data[2] == 0x67 && - eeprom_data[3] == 0x95) - start = 0xa0; /* Generic em28xx offset */ - else if ((eeprom_data[0] & 0xe1) == 0x01 && - eeprom_data[1] == 0x00 && - eeprom_data[2] == 0x00 && - eeprom_data[8] == 0x84) - start = 8; /* Generic cx2388x offset */ - else if (eeprom_data[1] == 0x70 && - eeprom_data[2] == 0x00 && - eeprom_data[4] == 0x74 && - eeprom_data[8] == 0x84) - start = 8; /* Generic cx23418 offset (models 74xxx) */ - else - start = 0; - - for (i = start; !done && i < 256; i += len) { - if (eeprom_data[i] == 0x84) { - len = eeprom_data[i + 1] + (eeprom_data[i + 2] << 8); - i += 3; - } else if ((eeprom_data[i] & 0xf0) == 0x70) { - if (eeprom_data[i] & 0x08) { - /* verify checksum! */ - done = 1; - break; - } - len = eeprom_data[i] & 0x07; - ++i; - } else { - tveeprom_warn("Encountered bad packet header [%02x]. " - "Corrupt or not a Hauppauge eeprom.\n", - eeprom_data[i]); - return; - } - - if (debug) { - tveeprom_info("Tag [%02x] + %d bytes:", - eeprom_data[i], len - 1); - for (j = 1; j < len; j++) - printk(KERN_CONT " %02x", eeprom_data[i + j]); - printk(KERN_CONT "\n"); - } - - /* process by tag */ - tag = eeprom_data[i]; - switch (tag) { - case 0x00: - /* tag: 'Comprehensive' */ - tuner1 = eeprom_data[i+6]; - t_format1 = eeprom_data[i+5]; - tvee->has_radio = eeprom_data[i+len-1]; - /* old style tag, don't know how to detect - IR presence, mark as unknown. */ - tvee->has_ir = 0; - tvee->model = - eeprom_data[i+8] + - (eeprom_data[i+9] << 8); - tvee->revision = eeprom_data[i+10] + - (eeprom_data[i+11] << 8) + - (eeprom_data[i+12] << 16); - break; - - case 0x01: - /* tag: 'SerialID' */ - tvee->serial_number = - eeprom_data[i+6] + - (eeprom_data[i+7] << 8) + - (eeprom_data[i+8] << 16); - break; - - case 0x02: - /* tag 'AudioInfo' - Note mask with 0x7F, high bit used on some older models - to indicate 4052 mux was removed in favor of using MSP - inputs directly. */ - audioic = eeprom_data[i+2] & 0x7f; - if (audioic < ARRAY_SIZE(audioIC)) - tvee->audio_processor = audioIC[audioic].id; - else - tvee->audio_processor = V4L2_IDENT_UNKNOWN; - break; - - /* case 0x03: tag 'EEInfo' */ - - case 0x04: - /* tag 'SerialID2' */ - tvee->serial_number = - eeprom_data[i+5] + - (eeprom_data[i+6] << 8) + - (eeprom_data[i+7] << 16); - - if ((eeprom_data[i + 8] & 0xf0) && - (tvee->serial_number < 0xffffff)) { - tvee->MAC_address[0] = 0x00; - tvee->MAC_address[1] = 0x0D; - tvee->MAC_address[2] = 0xFE; - tvee->MAC_address[3] = eeprom_data[i + 7]; - tvee->MAC_address[4] = eeprom_data[i + 6]; - tvee->MAC_address[5] = eeprom_data[i + 5]; - tvee->has_MAC_address = 1; - } - break; - - case 0x05: - /* tag 'Audio2' - Note mask with 0x7F, high bit used on some older models - to indicate 4052 mux was removed in favor of using MSP - inputs directly. */ - audioic = eeprom_data[i+1] & 0x7f; - if (audioic < ARRAY_SIZE(audioIC)) - tvee->audio_processor = audioIC[audioic].id; - else - tvee->audio_processor = V4L2_IDENT_UNKNOWN; - - break; - - case 0x06: - /* tag 'ModelRev' */ - tvee->model = - eeprom_data[i + 1] + - (eeprom_data[i + 2] << 8) + - (eeprom_data[i + 3] << 16) + - (eeprom_data[i + 4] << 24); - tvee->revision = - eeprom_data[i + 5] + - (eeprom_data[i + 6] << 8) + - (eeprom_data[i + 7] << 16); - break; - - case 0x07: - /* tag 'Details': according to Hauppauge not interesting - on any PCI-era or later boards. */ - break; - - /* there is no tag 0x08 defined */ - - case 0x09: - /* tag 'Video' */ - tvee->decoder_processor = eeprom_data[i + 1]; - break; - - case 0x0a: - /* tag 'Tuner' */ - if (beenhere == 0) { - tuner1 = eeprom_data[i + 2]; - t_format1 = eeprom_data[i + 1]; - beenhere = 1; - } else { - /* a second (radio) tuner may be present */ - tuner2 = eeprom_data[i + 2]; - t_format2 = eeprom_data[i + 1]; - /* not a TV tuner? */ - if (t_format2 == 0) - tvee->has_radio = 1; /* must be radio */ - } - break; - - case 0x0b: - /* tag 'Inputs': according to Hauppauge this is specific - to each driver family, so no good assumptions can be - made. */ - break; - - /* case 0x0c: tag 'Balun' */ - /* case 0x0d: tag 'Teletext' */ - - case 0x0e: - /* tag: 'Radio' */ - tvee->has_radio = eeprom_data[i+1]; - break; - - case 0x0f: - /* tag 'IRInfo' */ - tvee->has_ir = 1 | (eeprom_data[i+1] << 1); - break; - - /* case 0x10: tag 'VBIInfo' */ - /* case 0x11: tag 'QCInfo' */ - /* case 0x12: tag 'InfoBits' */ - - default: - tveeprom_dbg("Not sure what to do with tag [%02x]\n", - tag); - /* dump the rest of the packet? */ - } - } - - if (!done) { - tveeprom_warn("Ran out of data!\n"); - return; - } - - if (tvee->revision != 0) { - tvee->rev_str[0] = 32 + ((tvee->revision >> 18) & 0x3f); - tvee->rev_str[1] = 32 + ((tvee->revision >> 12) & 0x3f); - tvee->rev_str[2] = 32 + ((tvee->revision >> 6) & 0x3f); - tvee->rev_str[3] = 32 + (tvee->revision & 0x3f); - tvee->rev_str[4] = 0; - } - - if (hasRadioTuner(tuner1) && !tvee->has_radio) { - tveeprom_info("The eeprom says no radio is present, but the tuner type\n"); - tveeprom_info("indicates otherwise. I will assume that radio is present.\n"); - tvee->has_radio = 1; - } - - if (tuner1 < ARRAY_SIZE(hauppauge_tuner)) { - tvee->tuner_type = hauppauge_tuner[tuner1].id; - t_name1 = hauppauge_tuner[tuner1].name; - } else { - t_name1 = "unknown"; - } - - if (tuner2 < ARRAY_SIZE(hauppauge_tuner)) { - tvee->tuner2_type = hauppauge_tuner[tuner2].id; - t_name2 = hauppauge_tuner[tuner2].name; - } else { - t_name2 = "unknown"; - } - - tvee->tuner_hauppauge_model = tuner1; - tvee->tuner2_hauppauge_model = tuner2; - tvee->tuner_formats = 0; - tvee->tuner2_formats = 0; - for (i = j = 0; i < 8; i++) { - if (t_format1 & (1 << i)) { - tvee->tuner_formats |= hauppauge_tuner_fmt[i].id; - t_fmt_name1[j++] = hauppauge_tuner_fmt[i].name; - } - } - for (i = j = 0; i < 8; i++) { - if (t_format2 & (1 << i)) { - tvee->tuner2_formats |= hauppauge_tuner_fmt[i].id; - t_fmt_name2[j++] = hauppauge_tuner_fmt[i].name; - } - } - - tveeprom_info("Hauppauge model %d, rev %s, serial# %d\n", - tvee->model, tvee->rev_str, tvee->serial_number); - if (tvee->has_MAC_address == 1) - tveeprom_info("MAC address is %pM\n", tvee->MAC_address); - tveeprom_info("tuner model is %s (idx %d, type %d)\n", - t_name1, tuner1, tvee->tuner_type); - tveeprom_info("TV standards%s%s%s%s%s%s%s%s (eeprom 0x%02x)\n", - t_fmt_name1[0], t_fmt_name1[1], t_fmt_name1[2], - t_fmt_name1[3], t_fmt_name1[4], t_fmt_name1[5], - t_fmt_name1[6], t_fmt_name1[7], t_format1); - if (tuner2) - tveeprom_info("second tuner model is %s (idx %d, type %d)\n", - t_name2, tuner2, tvee->tuner2_type); - if (t_format2) - tveeprom_info("TV standards%s%s%s%s%s%s%s%s (eeprom 0x%02x)\n", - t_fmt_name2[0], t_fmt_name2[1], t_fmt_name2[2], - t_fmt_name2[3], t_fmt_name2[4], t_fmt_name2[5], - t_fmt_name2[6], t_fmt_name2[7], t_format2); - if (audioic < 0) { - tveeprom_info("audio processor is unknown (no idx)\n"); - tvee->audio_processor = V4L2_IDENT_UNKNOWN; - } else { - if (audioic < ARRAY_SIZE(audioIC)) - tveeprom_info("audio processor is %s (idx %d)\n", - audioIC[audioic].name, audioic); - else - tveeprom_info("audio processor is unknown (idx %d)\n", - audioic); - } - if (tvee->decoder_processor) - tveeprom_info("decoder processor is %s (idx %d)\n", - STRM(decoderIC, tvee->decoder_processor), - tvee->decoder_processor); - if (tvee->has_ir) - tveeprom_info("has %sradio, has %sIR receiver, has %sIR transmitter\n", - tvee->has_radio ? "" : "no ", - (tvee->has_ir & 2) ? "" : "no ", - (tvee->has_ir & 4) ? "" : "no "); - else - tveeprom_info("has %sradio\n", - tvee->has_radio ? "" : "no "); -} -EXPORT_SYMBOL(tveeprom_hauppauge_analog); - -/* ----------------------------------------------------------------------- */ -/* generic helper functions */ - -int tveeprom_read(struct i2c_client *c, unsigned char *eedata, int len) -{ - unsigned char buf; - int err; - - buf = 0; - err = i2c_master_send(c, &buf, 1); - if (err != 1) { - tveeprom_info("Huh, no eeprom present (err=%d)?\n", err); - return -1; - } - err = i2c_master_recv(c, eedata, len); - if (err != len) { - tveeprom_warn("i2c eeprom read error (err=%d)\n", err); - return -1; - } - if (debug) { - int i; - - tveeprom_info("full 256-byte eeprom dump:\n"); - for (i = 0; i < len; i++) { - if (0 == (i % 16)) - tveeprom_info("%02x:", i); - printk(KERN_CONT " %02x", eedata[i]); - if (15 == (i % 16)) - printk(KERN_CONT "\n"); - } - } - return 0; -} -EXPORT_SYMBOL(tveeprom_read); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/video/tvp514x.c b/drivers/media/video/tvp514x.c deleted file mode 100644 index cd615c1d601..00000000000 --- a/drivers/media/video/tvp514x.c +++ /dev/null @@ -1,1166 +0,0 @@ -/* - * drivers/media/video/tvp514x.c - * - * TI TVP5146/47 decoder driver - * - * Copyright (C) 2008 Texas Instruments Inc - * Author: Vaibhav Hiremath - * - * Contributors: - * Sivaraj R - * Brijesh R Jadav - * Hardik Shah - * Manjunath Hadli - * Karicheri Muralidharan - * - * This package is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * 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 -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "tvp514x_regs.h" - -/* Module Name */ -#define TVP514X_MODULE_NAME "tvp514x" - -/* Private macros for TVP */ -#define I2C_RETRY_COUNT (5) -#define LOCK_RETRY_COUNT (5) -#define LOCK_RETRY_DELAY (200) - -/* Debug functions */ -static bool debug; -module_param(debug, bool, 0644); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - -MODULE_AUTHOR("Texas Instruments"); -MODULE_DESCRIPTION("TVP514X linux decoder driver"); -MODULE_LICENSE("GPL"); - -/* enum tvp514x_std - enum for supported standards */ -enum tvp514x_std { - STD_NTSC_MJ = 0, - STD_PAL_BDGHIN, - STD_INVALID -}; - -/** - * struct tvp514x_std_info - Structure to store standard informations - * @width: Line width in pixels - * @height:Number of active lines - * @video_std: Value to write in REG_VIDEO_STD register - * @standard: v4l2 standard structure information - */ -struct tvp514x_std_info { - unsigned long width; - unsigned long height; - u8 video_std; - struct v4l2_standard standard; -}; - -static struct tvp514x_reg tvp514x_reg_list_default[0x40]; - -static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable); -/** - * struct tvp514x_decoder - TVP5146/47 decoder object - * @sd: Subdevice Slave handle - * @tvp514x_regs: copy of hw's regs with preset values. - * @pdata: Board specific - * @ver: Chip version - * @streaming: TVP5146/47 decoder streaming - enabled or disabled. - * @current_std: Current standard - * @num_stds: Number of standards - * @std_list: Standards list - * @input: Input routing at chip level - * @output: Output routing at chip level - */ -struct tvp514x_decoder { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - struct tvp514x_reg tvp514x_regs[ARRAY_SIZE(tvp514x_reg_list_default)]; - const struct tvp514x_platform_data *pdata; - - int ver; - int streaming; - - enum tvp514x_std current_std; - int num_stds; - const struct tvp514x_std_info *std_list; - /* Input and Output Routing parameters */ - u32 input; - u32 output; -}; - -/* TVP514x default register values */ -static struct tvp514x_reg tvp514x_reg_list_default[] = { - /* Composite selected */ - {TOK_WRITE, REG_INPUT_SEL, 0x05}, - {TOK_WRITE, REG_AFE_GAIN_CTRL, 0x0F}, - /* Auto mode */ - {TOK_WRITE, REG_VIDEO_STD, 0x00}, - {TOK_WRITE, REG_OPERATION_MODE, 0x00}, - {TOK_SKIP, REG_AUTOSWITCH_MASK, 0x3F}, - {TOK_WRITE, REG_COLOR_KILLER, 0x10}, - {TOK_WRITE, REG_LUMA_CONTROL1, 0x00}, - {TOK_WRITE, REG_LUMA_CONTROL2, 0x00}, - {TOK_WRITE, REG_LUMA_CONTROL3, 0x02}, - {TOK_WRITE, REG_BRIGHTNESS, 0x80}, - {TOK_WRITE, REG_CONTRAST, 0x80}, - {TOK_WRITE, REG_SATURATION, 0x80}, - {TOK_WRITE, REG_HUE, 0x00}, - {TOK_WRITE, REG_CHROMA_CONTROL1, 0x00}, - {TOK_WRITE, REG_CHROMA_CONTROL2, 0x0E}, - /* Reserved */ - {TOK_SKIP, 0x0F, 0x00}, - {TOK_WRITE, REG_COMP_PR_SATURATION, 0x80}, - {TOK_WRITE, REG_COMP_Y_CONTRAST, 0x80}, - {TOK_WRITE, REG_COMP_PB_SATURATION, 0x80}, - /* Reserved */ - {TOK_SKIP, 0x13, 0x00}, - {TOK_WRITE, REG_COMP_Y_BRIGHTNESS, 0x80}, - /* Reserved */ - {TOK_SKIP, 0x15, 0x00}, - /* NTSC timing */ - {TOK_SKIP, REG_AVID_START_PIXEL_LSB, 0x55}, - {TOK_SKIP, REG_AVID_START_PIXEL_MSB, 0x00}, - {TOK_SKIP, REG_AVID_STOP_PIXEL_LSB, 0x25}, - {TOK_SKIP, REG_AVID_STOP_PIXEL_MSB, 0x03}, - /* NTSC timing */ - {TOK_SKIP, REG_HSYNC_START_PIXEL_LSB, 0x00}, - {TOK_SKIP, REG_HSYNC_START_PIXEL_MSB, 0x00}, - {TOK_SKIP, REG_HSYNC_STOP_PIXEL_LSB, 0x40}, - {TOK_SKIP, REG_HSYNC_STOP_PIXEL_MSB, 0x00}, - /* NTSC timing */ - {TOK_SKIP, REG_VSYNC_START_LINE_LSB, 0x04}, - {TOK_SKIP, REG_VSYNC_START_LINE_MSB, 0x00}, - {TOK_SKIP, REG_VSYNC_STOP_LINE_LSB, 0x07}, - {TOK_SKIP, REG_VSYNC_STOP_LINE_MSB, 0x00}, - /* NTSC timing */ - {TOK_SKIP, REG_VBLK_START_LINE_LSB, 0x01}, - {TOK_SKIP, REG_VBLK_START_LINE_MSB, 0x00}, - {TOK_SKIP, REG_VBLK_STOP_LINE_LSB, 0x15}, - {TOK_SKIP, REG_VBLK_STOP_LINE_MSB, 0x00}, - /* Reserved */ - {TOK_SKIP, 0x26, 0x00}, - /* Reserved */ - {TOK_SKIP, 0x27, 0x00}, - {TOK_SKIP, REG_FAST_SWTICH_CONTROL, 0xCC}, - /* Reserved */ - {TOK_SKIP, 0x29, 0x00}, - {TOK_SKIP, REG_FAST_SWTICH_SCART_DELAY, 0x00}, - /* Reserved */ - {TOK_SKIP, 0x2B, 0x00}, - {TOK_SKIP, REG_SCART_DELAY, 0x00}, - {TOK_SKIP, REG_CTI_DELAY, 0x00}, - {TOK_SKIP, REG_CTI_CONTROL, 0x00}, - /* Reserved */ - {TOK_SKIP, 0x2F, 0x00}, - /* Reserved */ - {TOK_SKIP, 0x30, 0x00}, - /* Reserved */ - {TOK_SKIP, 0x31, 0x00}, - /* HS, VS active high */ - {TOK_WRITE, REG_SYNC_CONTROL, 0x00}, - /* 10-bit BT.656 */ - {TOK_WRITE, REG_OUTPUT_FORMATTER1, 0x00}, - /* Enable clk & data */ - {TOK_WRITE, REG_OUTPUT_FORMATTER2, 0x11}, - /* Enable AVID & FLD */ - {TOK_WRITE, REG_OUTPUT_FORMATTER3, 0xEE}, - /* Enable VS & HS */ - {TOK_WRITE, REG_OUTPUT_FORMATTER4, 0xAF}, - {TOK_WRITE, REG_OUTPUT_FORMATTER5, 0xFF}, - {TOK_WRITE, REG_OUTPUT_FORMATTER6, 0xFF}, - /* Clear status */ - {TOK_WRITE, REG_CLEAR_LOST_LOCK, 0x01}, - {TOK_TERM, 0, 0}, -}; - -/** - * Supported standards - - * - * Currently supports two standards only, need to add support for rest of the - * modes, like SECAM, etc... - */ -static const struct tvp514x_std_info tvp514x_std_list[] = { - /* Standard: STD_NTSC_MJ */ - [STD_NTSC_MJ] = { - .width = NTSC_NUM_ACTIVE_PIXELS, - .height = NTSC_NUM_ACTIVE_LINES, - .video_std = VIDEO_STD_NTSC_MJ_BIT, - .standard = { - .index = 0, - .id = V4L2_STD_NTSC, - .name = "NTSC", - .frameperiod = {1001, 30000}, - .framelines = 525 - }, - /* Standard: STD_PAL_BDGHIN */ - }, - [STD_PAL_BDGHIN] = { - .width = PAL_NUM_ACTIVE_PIXELS, - .height = PAL_NUM_ACTIVE_LINES, - .video_std = VIDEO_STD_PAL_BDGHIN_BIT, - .standard = { - .index = 1, - .id = V4L2_STD_PAL, - .name = "PAL", - .frameperiod = {1, 25}, - .framelines = 625 - }, - }, - /* Standard: need to add for additional standard */ -}; - - -static inline struct tvp514x_decoder *to_decoder(struct v4l2_subdev *sd) -{ - return container_of(sd, struct tvp514x_decoder, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct tvp514x_decoder, hdl)->sd; -} - - -/** - * tvp514x_read_reg() - Read a value from a register in an TVP5146/47. - * @sd: ptr to v4l2_subdev struct - * @reg: TVP5146/47 register address - * - * Returns value read if successful, or non-zero (-1) otherwise. - */ -static int tvp514x_read_reg(struct v4l2_subdev *sd, u8 reg) -{ - int err, retry = 0; - struct i2c_client *client = v4l2_get_subdevdata(sd); - -read_again: - - err = i2c_smbus_read_byte_data(client, reg); - if (err < 0) { - if (retry <= I2C_RETRY_COUNT) { - v4l2_warn(sd, "Read: retry ... %d\n", retry); - retry++; - msleep_interruptible(10); - goto read_again; - } - } - - return err; -} - -/** - * dump_reg() - dump the register content of TVP5146/47. - * @sd: ptr to v4l2_subdev struct - * @reg: TVP5146/47 register address - */ -static void dump_reg(struct v4l2_subdev *sd, u8 reg) -{ - u32 val; - - val = tvp514x_read_reg(sd, reg); - v4l2_info(sd, "Reg(0x%.2X): 0x%.2X\n", reg, val); -} - -/** - * tvp514x_write_reg() - Write a value to a register in TVP5146/47 - * @sd: ptr to v4l2_subdev struct - * @reg: TVP5146/47 register address - * @val: value to be written to the register - * - * Write a value to a register in an TVP5146/47 decoder device. - * Returns zero if successful, or non-zero otherwise. - */ -static int tvp514x_write_reg(struct v4l2_subdev *sd, u8 reg, u8 val) -{ - int err, retry = 0; - struct i2c_client *client = v4l2_get_subdevdata(sd); - -write_again: - - err = i2c_smbus_write_byte_data(client, reg, val); - if (err) { - if (retry <= I2C_RETRY_COUNT) { - v4l2_warn(sd, "Write: retry ... %d\n", retry); - retry++; - msleep_interruptible(10); - goto write_again; - } - } - - return err; -} - -/** - * tvp514x_write_regs() : Initializes a list of TVP5146/47 registers - * @sd: ptr to v4l2_subdev struct - * @reglist: list of TVP5146/47 registers and values - * - * Initializes a list of TVP5146/47 registers:- - * if token is TOK_TERM, then entire write operation terminates - * if token is TOK_DELAY, then a delay of 'val' msec is introduced - * if token is TOK_SKIP, then the register write is skipped - * if token is TOK_WRITE, then the register write is performed - * Returns zero if successful, or non-zero otherwise. - */ -static int tvp514x_write_regs(struct v4l2_subdev *sd, - const struct tvp514x_reg reglist[]) -{ - int err; - const struct tvp514x_reg *next = reglist; - - for (; next->token != TOK_TERM; next++) { - if (next->token == TOK_DELAY) { - msleep(next->val); - continue; - } - - if (next->token == TOK_SKIP) - continue; - - err = tvp514x_write_reg(sd, next->reg, (u8) next->val); - if (err) { - v4l2_err(sd, "Write failed. Err[%d]\n", err); - return err; - } - } - return 0; -} - -/** - * tvp514x_query_current_std() : Query the current standard detected by TVP5146/47 - * @sd: ptr to v4l2_subdev struct - * - * Returns the current standard detected by TVP5146/47, STD_INVALID if there is no - * standard detected. - */ -static enum tvp514x_std tvp514x_query_current_std(struct v4l2_subdev *sd) -{ - u8 std, std_status; - - std = tvp514x_read_reg(sd, REG_VIDEO_STD); - if ((std & VIDEO_STD_MASK) == VIDEO_STD_AUTO_SWITCH_BIT) - /* use the standard status register */ - std_status = tvp514x_read_reg(sd, REG_VIDEO_STD_STATUS); - else - /* use the standard register itself */ - std_status = std; - - switch (std_status & VIDEO_STD_MASK) { - case VIDEO_STD_NTSC_MJ_BIT: - return STD_NTSC_MJ; - - case VIDEO_STD_PAL_BDGHIN_BIT: - return STD_PAL_BDGHIN; - - default: - return STD_INVALID; - } - - return STD_INVALID; -} - -/* TVP5146/47 register dump function */ -static void tvp514x_reg_dump(struct v4l2_subdev *sd) -{ - dump_reg(sd, REG_INPUT_SEL); - dump_reg(sd, REG_AFE_GAIN_CTRL); - dump_reg(sd, REG_VIDEO_STD); - dump_reg(sd, REG_OPERATION_MODE); - dump_reg(sd, REG_COLOR_KILLER); - dump_reg(sd, REG_LUMA_CONTROL1); - dump_reg(sd, REG_LUMA_CONTROL2); - dump_reg(sd, REG_LUMA_CONTROL3); - dump_reg(sd, REG_BRIGHTNESS); - dump_reg(sd, REG_CONTRAST); - dump_reg(sd, REG_SATURATION); - dump_reg(sd, REG_HUE); - dump_reg(sd, REG_CHROMA_CONTROL1); - dump_reg(sd, REG_CHROMA_CONTROL2); - dump_reg(sd, REG_COMP_PR_SATURATION); - dump_reg(sd, REG_COMP_Y_CONTRAST); - dump_reg(sd, REG_COMP_PB_SATURATION); - dump_reg(sd, REG_COMP_Y_BRIGHTNESS); - dump_reg(sd, REG_AVID_START_PIXEL_LSB); - dump_reg(sd, REG_AVID_START_PIXEL_MSB); - dump_reg(sd, REG_AVID_STOP_PIXEL_LSB); - dump_reg(sd, REG_AVID_STOP_PIXEL_MSB); - dump_reg(sd, REG_HSYNC_START_PIXEL_LSB); - dump_reg(sd, REG_HSYNC_START_PIXEL_MSB); - dump_reg(sd, REG_HSYNC_STOP_PIXEL_LSB); - dump_reg(sd, REG_HSYNC_STOP_PIXEL_MSB); - dump_reg(sd, REG_VSYNC_START_LINE_LSB); - dump_reg(sd, REG_VSYNC_START_LINE_MSB); - dump_reg(sd, REG_VSYNC_STOP_LINE_LSB); - dump_reg(sd, REG_VSYNC_STOP_LINE_MSB); - dump_reg(sd, REG_VBLK_START_LINE_LSB); - dump_reg(sd, REG_VBLK_START_LINE_MSB); - dump_reg(sd, REG_VBLK_STOP_LINE_LSB); - dump_reg(sd, REG_VBLK_STOP_LINE_MSB); - dump_reg(sd, REG_SYNC_CONTROL); - dump_reg(sd, REG_OUTPUT_FORMATTER1); - dump_reg(sd, REG_OUTPUT_FORMATTER2); - dump_reg(sd, REG_OUTPUT_FORMATTER3); - dump_reg(sd, REG_OUTPUT_FORMATTER4); - dump_reg(sd, REG_OUTPUT_FORMATTER5); - dump_reg(sd, REG_OUTPUT_FORMATTER6); - dump_reg(sd, REG_CLEAR_LOST_LOCK); -} - -/** - * tvp514x_configure() - Configure the TVP5146/47 registers - * @sd: ptr to v4l2_subdev struct - * @decoder: ptr to tvp514x_decoder structure - * - * Returns zero if successful, or non-zero otherwise. - */ -static int tvp514x_configure(struct v4l2_subdev *sd, - struct tvp514x_decoder *decoder) -{ - int err; - - /* common register initialization */ - err = - tvp514x_write_regs(sd, decoder->tvp514x_regs); - if (err) - return err; - - if (debug) - tvp514x_reg_dump(sd); - - return 0; -} - -/** - * tvp514x_detect() - Detect if an tvp514x is present, and if so which revision. - * @sd: pointer to standard V4L2 sub-device structure - * @decoder: pointer to tvp514x_decoder structure - * - * A device is considered to be detected if the chip ID (LSB and MSB) - * registers match the expected values. - * Any value of the rom version register is accepted. - * Returns ENODEV error number if no device is detected, or zero - * if a device is detected. - */ -static int tvp514x_detect(struct v4l2_subdev *sd, - struct tvp514x_decoder *decoder) -{ - u8 chip_id_msb, chip_id_lsb, rom_ver; - struct i2c_client *client = v4l2_get_subdevdata(sd); - - chip_id_msb = tvp514x_read_reg(sd, REG_CHIP_ID_MSB); - chip_id_lsb = tvp514x_read_reg(sd, REG_CHIP_ID_LSB); - rom_ver = tvp514x_read_reg(sd, REG_ROM_VERSION); - - v4l2_dbg(1, debug, sd, - "chip id detected msb:0x%x lsb:0x%x rom version:0x%x\n", - chip_id_msb, chip_id_lsb, rom_ver); - if ((chip_id_msb != TVP514X_CHIP_ID_MSB) - || ((chip_id_lsb != TVP5146_CHIP_ID_LSB) - && (chip_id_lsb != TVP5147_CHIP_ID_LSB))) { - /* We didn't read the values we expected, so this must not be - * an TVP5146/47. - */ - v4l2_err(sd, "chip id mismatch msb:0x%x lsb:0x%x\n", - chip_id_msb, chip_id_lsb); - return -ENODEV; - } - - decoder->ver = rom_ver; - - v4l2_info(sd, "%s (Version - 0x%.2x) found at 0x%x (%s)\n", - client->name, decoder->ver, - client->addr << 1, client->adapter->name); - return 0; -} - -/** - * tvp514x_querystd() - V4L2 decoder interface handler for querystd - * @sd: pointer to standard V4L2 sub-device structure - * @std_id: standard V4L2 std_id ioctl enum - * - * Returns the current standard detected by TVP5146/47. If no active input is - * detected then *std_id is set to 0 and the function returns 0. - */ -static int tvp514x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std_id) -{ - struct tvp514x_decoder *decoder = to_decoder(sd); - enum tvp514x_std current_std; - enum tvp514x_input input_sel; - u8 sync_lock_status, lock_mask; - - if (std_id == NULL) - return -EINVAL; - - *std_id = V4L2_STD_UNKNOWN; - - /* query the current standard */ - current_std = tvp514x_query_current_std(sd); - if (current_std == STD_INVALID) - return 0; - - input_sel = decoder->input; - - switch (input_sel) { - case INPUT_CVBS_VI1A: - case INPUT_CVBS_VI1B: - case INPUT_CVBS_VI1C: - case INPUT_CVBS_VI2A: - case INPUT_CVBS_VI2B: - case INPUT_CVBS_VI2C: - case INPUT_CVBS_VI3A: - case INPUT_CVBS_VI3B: - case INPUT_CVBS_VI3C: - case INPUT_CVBS_VI4A: - lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT | - STATUS_HORZ_SYNC_LOCK_BIT | - STATUS_VIRT_SYNC_LOCK_BIT; - break; - - case INPUT_SVIDEO_VI2A_VI1A: - case INPUT_SVIDEO_VI2B_VI1B: - case INPUT_SVIDEO_VI2C_VI1C: - case INPUT_SVIDEO_VI2A_VI3A: - case INPUT_SVIDEO_VI2B_VI3B: - case INPUT_SVIDEO_VI2C_VI3C: - case INPUT_SVIDEO_VI4A_VI1A: - case INPUT_SVIDEO_VI4A_VI1B: - case INPUT_SVIDEO_VI4A_VI1C: - case INPUT_SVIDEO_VI4A_VI3A: - case INPUT_SVIDEO_VI4A_VI3B: - case INPUT_SVIDEO_VI4A_VI3C: - lock_mask = STATUS_HORZ_SYNC_LOCK_BIT | - STATUS_VIRT_SYNC_LOCK_BIT; - break; - /*Need to add other interfaces*/ - default: - return -EINVAL; - } - /* check whether signal is locked */ - sync_lock_status = tvp514x_read_reg(sd, REG_STATUS1); - if (lock_mask != (sync_lock_status & lock_mask)) - return 0; /* No input detected */ - - *std_id = decoder->std_list[current_std].standard.id; - - v4l2_dbg(1, debug, sd, "Current STD: %s\n", - decoder->std_list[current_std].standard.name); - return 0; -} - -/** - * tvp514x_s_std() - V4L2 decoder interface handler for s_std - * @sd: pointer to standard V4L2 sub-device structure - * @std_id: standard V4L2 v4l2_std_id ioctl enum - * - * If std_id is supported, sets the requested standard. Otherwise, returns - * -EINVAL - */ -static int tvp514x_s_std(struct v4l2_subdev *sd, v4l2_std_id std_id) -{ - struct tvp514x_decoder *decoder = to_decoder(sd); - int err, i; - - for (i = 0; i < decoder->num_stds; i++) - if (std_id & decoder->std_list[i].standard.id) - break; - - if ((i == decoder->num_stds) || (i == STD_INVALID)) - return -EINVAL; - - err = tvp514x_write_reg(sd, REG_VIDEO_STD, - decoder->std_list[i].video_std); - if (err) - return err; - - decoder->current_std = i; - decoder->tvp514x_regs[REG_VIDEO_STD].val = - decoder->std_list[i].video_std; - - v4l2_dbg(1, debug, sd, "Standard set to: %s\n", - decoder->std_list[i].standard.name); - return 0; -} - -/** - * tvp514x_s_routing() - V4L2 decoder interface handler for s_routing - * @sd: pointer to standard V4L2 sub-device structure - * @input: input selector for routing the signal - * @output: output selector for routing the signal - * @config: config value. Not used - * - * If index is valid, selects the requested input. Otherwise, returns -EINVAL if - * the input is not supported or there is no active signal present in the - * selected input. - */ -static int tvp514x_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct tvp514x_decoder *decoder = to_decoder(sd); - int err; - enum tvp514x_input input_sel; - enum tvp514x_output output_sel; - u8 sync_lock_status, lock_mask; - int try_count = LOCK_RETRY_COUNT; - - if ((input >= INPUT_INVALID) || - (output >= OUTPUT_INVALID)) - /* Index out of bound */ - return -EINVAL; - - /* - * For the sequence streamon -> streamoff and again s_input - * it fails to lock the signal, since streamoff puts TVP514x - * into power off state which leads to failure in sub-sequent s_input. - * - * So power up the TVP514x device here, since it is important to lock - * the signal at this stage. - */ - if (!decoder->streaming) - tvp514x_s_stream(sd, 1); - - input_sel = input; - output_sel = output; - - err = tvp514x_write_reg(sd, REG_INPUT_SEL, input_sel); - if (err) - return err; - - output_sel |= tvp514x_read_reg(sd, - REG_OUTPUT_FORMATTER1) & 0x7; - err = tvp514x_write_reg(sd, REG_OUTPUT_FORMATTER1, - output_sel); - if (err) - return err; - - decoder->tvp514x_regs[REG_INPUT_SEL].val = input_sel; - decoder->tvp514x_regs[REG_OUTPUT_FORMATTER1].val = output_sel; - - /* Clear status */ - msleep(LOCK_RETRY_DELAY); - err = - tvp514x_write_reg(sd, REG_CLEAR_LOST_LOCK, 0x01); - if (err) - return err; - - switch (input_sel) { - case INPUT_CVBS_VI1A: - case INPUT_CVBS_VI1B: - case INPUT_CVBS_VI1C: - case INPUT_CVBS_VI2A: - case INPUT_CVBS_VI2B: - case INPUT_CVBS_VI2C: - case INPUT_CVBS_VI3A: - case INPUT_CVBS_VI3B: - case INPUT_CVBS_VI3C: - case INPUT_CVBS_VI4A: - lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT | - STATUS_HORZ_SYNC_LOCK_BIT | - STATUS_VIRT_SYNC_LOCK_BIT; - break; - - case INPUT_SVIDEO_VI2A_VI1A: - case INPUT_SVIDEO_VI2B_VI1B: - case INPUT_SVIDEO_VI2C_VI1C: - case INPUT_SVIDEO_VI2A_VI3A: - case INPUT_SVIDEO_VI2B_VI3B: - case INPUT_SVIDEO_VI2C_VI3C: - case INPUT_SVIDEO_VI4A_VI1A: - case INPUT_SVIDEO_VI4A_VI1B: - case INPUT_SVIDEO_VI4A_VI1C: - case INPUT_SVIDEO_VI4A_VI3A: - case INPUT_SVIDEO_VI4A_VI3B: - case INPUT_SVIDEO_VI4A_VI3C: - lock_mask = STATUS_HORZ_SYNC_LOCK_BIT | - STATUS_VIRT_SYNC_LOCK_BIT; - break; - /* Need to add other interfaces*/ - default: - return -EINVAL; - } - - while (try_count-- > 0) { - /* Allow decoder to sync up with new input */ - msleep(LOCK_RETRY_DELAY); - - sync_lock_status = tvp514x_read_reg(sd, - REG_STATUS1); - if (lock_mask == (sync_lock_status & lock_mask)) - /* Input detected */ - break; - } - - if (try_count < 0) - return -EINVAL; - - decoder->input = input; - decoder->output = output; - - v4l2_dbg(1, debug, sd, "Input set to: %d\n", input_sel); - - return 0; -} - -/** - * tvp514x_s_ctrl() - V4L2 decoder interface handler for s_ctrl - * @ctrl: pointer to v4l2_ctrl structure - * - * If the requested control is supported, sets the control's current - * value in HW. Otherwise, returns -EINVAL if the control is not supported. - */ -static int tvp514x_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - struct tvp514x_decoder *decoder = to_decoder(sd); - int err = -EINVAL, value; - - value = ctrl->val; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - err = tvp514x_write_reg(sd, REG_BRIGHTNESS, value); - if (!err) - decoder->tvp514x_regs[REG_BRIGHTNESS].val = value; - break; - case V4L2_CID_CONTRAST: - err = tvp514x_write_reg(sd, REG_CONTRAST, value); - if (!err) - decoder->tvp514x_regs[REG_CONTRAST].val = value; - break; - case V4L2_CID_SATURATION: - err = tvp514x_write_reg(sd, REG_SATURATION, value); - if (!err) - decoder->tvp514x_regs[REG_SATURATION].val = value; - break; - case V4L2_CID_HUE: - if (value == 180) - value = 0x7F; - else if (value == -180) - value = 0x80; - err = tvp514x_write_reg(sd, REG_HUE, value); - if (!err) - decoder->tvp514x_regs[REG_HUE].val = value; - break; - case V4L2_CID_AUTOGAIN: - err = tvp514x_write_reg(sd, REG_AFE_GAIN_CTRL, value ? 0x0f : 0x0c); - if (!err) - decoder->tvp514x_regs[REG_AFE_GAIN_CTRL].val = value; - break; - } - - v4l2_dbg(1, debug, sd, "Set Control: ID - %d - %d\n", - ctrl->id, ctrl->val); - return err; -} - -/** - * tvp514x_enum_mbus_fmt() - V4L2 decoder interface handler for enum_mbus_fmt - * @sd: pointer to standard V4L2 sub-device structure - * @index: index of pixelcode to retrieve - * @code: receives the pixelcode - * - * Enumerates supported mediabus formats - */ -static int -tvp514x_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, - enum v4l2_mbus_pixelcode *code) -{ - if (index) - return -EINVAL; - - *code = V4L2_MBUS_FMT_YUYV10_2X10; - return 0; -} - -/** - * tvp514x_mbus_fmt_cap() - V4L2 decoder interface handler for try/s/g_mbus_fmt - * @sd: pointer to standard V4L2 sub-device structure - * @f: pointer to the mediabus format structure - * - * Negotiates the image capture size and mediabus format. - */ -static int -tvp514x_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) -{ - struct tvp514x_decoder *decoder = to_decoder(sd); - enum tvp514x_std current_std; - - if (f == NULL) - return -EINVAL; - - /* Calculate height and width based on current standard */ - current_std = decoder->current_std; - - f->code = V4L2_MBUS_FMT_YUYV10_2X10; - f->width = decoder->std_list[current_std].width; - f->height = decoder->std_list[current_std].height; - f->field = V4L2_FIELD_INTERLACED; - f->colorspace = V4L2_COLORSPACE_SMPTE170M; - - v4l2_dbg(1, debug, sd, "MBUS_FMT: Width - %d, Height - %d\n", - f->width, f->height); - return 0; -} - -/** - * tvp514x_g_parm() - V4L2 decoder interface handler for g_parm - * @sd: pointer to standard V4L2 sub-device structure - * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure - * - * Returns the decoder's video CAPTURE parameters. - */ -static int -tvp514x_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a) -{ - struct tvp514x_decoder *decoder = to_decoder(sd); - struct v4l2_captureparm *cparm; - enum tvp514x_std current_std; - - if (a == NULL) - return -EINVAL; - - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - /* only capture is supported */ - return -EINVAL; - - /* get the current standard */ - current_std = decoder->current_std; - - cparm = &a->parm.capture; - cparm->capability = V4L2_CAP_TIMEPERFRAME; - cparm->timeperframe = - decoder->std_list[current_std].standard.frameperiod; - - return 0; -} - -/** - * tvp514x_s_parm() - V4L2 decoder interface handler for s_parm - * @sd: pointer to standard V4L2 sub-device structure - * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure - * - * Configures the decoder to use the input parameters, if possible. If - * not possible, returns the appropriate error code. - */ -static int -tvp514x_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a) -{ - struct tvp514x_decoder *decoder = to_decoder(sd); - struct v4l2_fract *timeperframe; - enum tvp514x_std current_std; - - if (a == NULL) - return -EINVAL; - - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - /* only capture is supported */ - return -EINVAL; - - timeperframe = &a->parm.capture.timeperframe; - - /* get the current standard */ - current_std = decoder->current_std; - - *timeperframe = - decoder->std_list[current_std].standard.frameperiod; - - return 0; -} - -/** - * tvp514x_s_stream() - V4L2 decoder i/f handler for s_stream - * @sd: pointer to standard V4L2 sub-device structure - * @enable: streaming enable or disable - * - * Sets streaming to enable or disable, if possible. - */ -static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable) -{ - int err = 0; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct tvp514x_decoder *decoder = to_decoder(sd); - - if (decoder->streaming == enable) - return 0; - - switch (enable) { - case 0: - { - /* Power Down Sequence */ - err = tvp514x_write_reg(sd, REG_OPERATION_MODE, 0x01); - if (err) { - v4l2_err(sd, "Unable to turn off decoder\n"); - return err; - } - decoder->streaming = enable; - break; - } - case 1: - { - struct tvp514x_reg *int_seq = (struct tvp514x_reg *) - client->driver->id_table->driver_data; - - /* Power Up Sequence */ - err = tvp514x_write_regs(sd, int_seq); - if (err) { - v4l2_err(sd, "Unable to turn on decoder\n"); - return err; - } - /* Detect if not already detected */ - err = tvp514x_detect(sd, decoder); - if (err) { - v4l2_err(sd, "Unable to detect decoder\n"); - return err; - } - err = tvp514x_configure(sd, decoder); - if (err) { - v4l2_err(sd, "Unable to configure decoder\n"); - return err; - } - decoder->streaming = enable; - break; - } - default: - err = -ENODEV; - break; - } - - return err; -} - -static const struct v4l2_ctrl_ops tvp514x_ctrl_ops = { - .s_ctrl = tvp514x_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops tvp514x_core_ops = { - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, - .s_std = tvp514x_s_std, -}; - -static const struct v4l2_subdev_video_ops tvp514x_video_ops = { - .s_routing = tvp514x_s_routing, - .querystd = tvp514x_querystd, - .enum_mbus_fmt = tvp514x_enum_mbus_fmt, - .g_mbus_fmt = tvp514x_mbus_fmt, - .try_mbus_fmt = tvp514x_mbus_fmt, - .s_mbus_fmt = tvp514x_mbus_fmt, - .g_parm = tvp514x_g_parm, - .s_parm = tvp514x_s_parm, - .s_stream = tvp514x_s_stream, -}; - -static const struct v4l2_subdev_ops tvp514x_ops = { - .core = &tvp514x_core_ops, - .video = &tvp514x_video_ops, -}; - -static struct tvp514x_decoder tvp514x_dev = { - .streaming = 0, - .current_std = STD_NTSC_MJ, - .std_list = tvp514x_std_list, - .num_stds = ARRAY_SIZE(tvp514x_std_list), - -}; - -/** - * tvp514x_probe() - decoder driver i2c probe handler - * @client: i2c driver client device structure - * @id: i2c driver id table - * - * Register decoder as an i2c client device and V4L2 - * device. - */ -static int -tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ - struct tvp514x_decoder *decoder; - struct v4l2_subdev *sd; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - if (!client->dev.platform_data) { - v4l2_err(client, "No platform data!!\n"); - return -ENODEV; - } - - decoder = kzalloc(sizeof(*decoder), GFP_KERNEL); - if (!decoder) - return -ENOMEM; - - /* Initialize the tvp514x_decoder with default configuration */ - *decoder = tvp514x_dev; - /* Copy default register configuration */ - memcpy(decoder->tvp514x_regs, tvp514x_reg_list_default, - sizeof(tvp514x_reg_list_default)); - - /* Copy board specific information here */ - decoder->pdata = client->dev.platform_data; - - /** - * Fetch platform specific data, and configure the - * tvp514x_reg_list[] accordingly. Since this is one - * time configuration, no need to preserve. - */ - decoder->tvp514x_regs[REG_OUTPUT_FORMATTER2].val |= - (decoder->pdata->clk_polarity << 1); - decoder->tvp514x_regs[REG_SYNC_CONTROL].val |= - ((decoder->pdata->hs_polarity << 2) | - (decoder->pdata->vs_polarity << 3)); - /* Set default standard to auto */ - decoder->tvp514x_regs[REG_VIDEO_STD].val = - VIDEO_STD_AUTO_SWITCH_BIT; - - /* Register with V4L2 layer as slave device */ - sd = &decoder->sd; - v4l2_i2c_subdev_init(sd, client, &tvp514x_ops); - - v4l2_ctrl_handler_init(&decoder->hdl, 5); - v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); - v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, 128); - v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, - V4L2_CID_SATURATION, 0, 255, 1, 128); - v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, - V4L2_CID_HUE, -180, 180, 180, 0); - v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - sd->ctrl_handler = &decoder->hdl; - if (decoder->hdl.error) { - int err = decoder->hdl.error; - - v4l2_ctrl_handler_free(&decoder->hdl); - kfree(decoder); - return err; - } - v4l2_ctrl_handler_setup(&decoder->hdl); - - v4l2_info(sd, "%s decoder driver registered !!\n", sd->name); - - return 0; - -} - -/** - * tvp514x_remove() - decoder driver i2c remove handler - * @client: i2c driver client device structure - * - * Unregister decoder as an i2c client device and V4L2 - * device. Complement of tvp514x_probe(). - */ -static int tvp514x_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct tvp514x_decoder *decoder = to_decoder(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&decoder->hdl); - kfree(decoder); - return 0; -} -/* TVP5146 Init/Power on Sequence */ -static const struct tvp514x_reg tvp5146_init_reg_seq[] = { - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80}, - {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0}, - {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, - {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00}, - {TOK_WRITE, REG_OPERATION_MODE, 0x01}, - {TOK_WRITE, REG_OPERATION_MODE, 0x00}, - {TOK_TERM, 0, 0}, -}; - -/* TVP5147 Init/Power on Sequence */ -static const struct tvp514x_reg tvp5147_init_reg_seq[] = { - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80}, - {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0}, - {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x16}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xA0}, - {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x16}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0}, - {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00}, - {TOK_WRITE, REG_OPERATION_MODE, 0x01}, - {TOK_WRITE, REG_OPERATION_MODE, 0x00}, - {TOK_TERM, 0, 0}, -}; - -/* TVP5146M2/TVP5147M1 Init/Power on Sequence */ -static const struct tvp514x_reg tvp514xm_init_reg_seq[] = { - {TOK_WRITE, REG_OPERATION_MODE, 0x01}, - {TOK_WRITE, REG_OPERATION_MODE, 0x00}, - {TOK_TERM, 0, 0}, -}; - -/** - * I2C Device Table - - * - * name - Name of the actual device/chip. - * driver_data - Driver data - */ -static const struct i2c_device_id tvp514x_id[] = { - {"tvp5146", (unsigned long)tvp5146_init_reg_seq}, - {"tvp5146m2", (unsigned long)tvp514xm_init_reg_seq}, - {"tvp5147", (unsigned long)tvp5147_init_reg_seq}, - {"tvp5147m1", (unsigned long)tvp514xm_init_reg_seq}, - {}, -}; - -MODULE_DEVICE_TABLE(i2c, tvp514x_id); - -static struct i2c_driver tvp514x_driver = { - .driver = { - .owner = THIS_MODULE, - .name = TVP514X_MODULE_NAME, - }, - .probe = tvp514x_probe, - .remove = tvp514x_remove, - .id_table = tvp514x_id, -}; - -module_i2c_driver(tvp514x_driver); diff --git a/drivers/media/video/tvp514x_regs.h b/drivers/media/video/tvp514x_regs.h deleted file mode 100644 index 18f29ad0dfe..00000000000 --- a/drivers/media/video/tvp514x_regs.h +++ /dev/null @@ -1,287 +0,0 @@ -/* - * drivers/media/video/tvp514x_regs.h - * - * Copyright (C) 2008 Texas Instruments Inc - * Author: Vaibhav Hiremath - * - * Contributors: - * Sivaraj R - * Brijesh R Jadav - * Hardik Shah - * Manjunath Hadli - * Karicheri Muralidharan - * - * This package is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * 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. - * - */ - -#ifndef _TVP514X_REGS_H -#define _TVP514X_REGS_H - -/* - * TVP5146/47 registers - */ -#define REG_INPUT_SEL (0x00) -#define REG_AFE_GAIN_CTRL (0x01) -#define REG_VIDEO_STD (0x02) -#define REG_OPERATION_MODE (0x03) -#define REG_AUTOSWITCH_MASK (0x04) - -#define REG_COLOR_KILLER (0x05) -#define REG_LUMA_CONTROL1 (0x06) -#define REG_LUMA_CONTROL2 (0x07) -#define REG_LUMA_CONTROL3 (0x08) - -#define REG_BRIGHTNESS (0x09) -#define REG_CONTRAST (0x0A) -#define REG_SATURATION (0x0B) -#define REG_HUE (0x0C) - -#define REG_CHROMA_CONTROL1 (0x0D) -#define REG_CHROMA_CONTROL2 (0x0E) - -/* 0x0F Reserved */ - -#define REG_COMP_PR_SATURATION (0x10) -#define REG_COMP_Y_CONTRAST (0x11) -#define REG_COMP_PB_SATURATION (0x12) - -/* 0x13 Reserved */ - -#define REG_COMP_Y_BRIGHTNESS (0x14) - -/* 0x15 Reserved */ - -#define REG_AVID_START_PIXEL_LSB (0x16) -#define REG_AVID_START_PIXEL_MSB (0x17) -#define REG_AVID_STOP_PIXEL_LSB (0x18) -#define REG_AVID_STOP_PIXEL_MSB (0x19) - -#define REG_HSYNC_START_PIXEL_LSB (0x1A) -#define REG_HSYNC_START_PIXEL_MSB (0x1B) -#define REG_HSYNC_STOP_PIXEL_LSB (0x1C) -#define REG_HSYNC_STOP_PIXEL_MSB (0x1D) - -#define REG_VSYNC_START_LINE_LSB (0x1E) -#define REG_VSYNC_START_LINE_MSB (0x1F) -#define REG_VSYNC_STOP_LINE_LSB (0x20) -#define REG_VSYNC_STOP_LINE_MSB (0x21) - -#define REG_VBLK_START_LINE_LSB (0x22) -#define REG_VBLK_START_LINE_MSB (0x23) -#define REG_VBLK_STOP_LINE_LSB (0x24) -#define REG_VBLK_STOP_LINE_MSB (0x25) - -/* 0x26 - 0x27 Reserved */ - -#define REG_FAST_SWTICH_CONTROL (0x28) - -/* 0x29 Reserved */ - -#define REG_FAST_SWTICH_SCART_DELAY (0x2A) - -/* 0x2B Reserved */ - -#define REG_SCART_DELAY (0x2C) -#define REG_CTI_DELAY (0x2D) -#define REG_CTI_CONTROL (0x2E) - -/* 0x2F - 0x31 Reserved */ - -#define REG_SYNC_CONTROL (0x32) -#define REG_OUTPUT_FORMATTER1 (0x33) -#define REG_OUTPUT_FORMATTER2 (0x34) -#define REG_OUTPUT_FORMATTER3 (0x35) -#define REG_OUTPUT_FORMATTER4 (0x36) -#define REG_OUTPUT_FORMATTER5 (0x37) -#define REG_OUTPUT_FORMATTER6 (0x38) -#define REG_CLEAR_LOST_LOCK (0x39) - -#define REG_STATUS1 (0x3A) -#define REG_STATUS2 (0x3B) - -#define REG_AGC_GAIN_STATUS_LSB (0x3C) -#define REG_AGC_GAIN_STATUS_MSB (0x3D) - -/* 0x3E Reserved */ - -#define REG_VIDEO_STD_STATUS (0x3F) -#define REG_GPIO_INPUT1 (0x40) -#define REG_GPIO_INPUT2 (0x41) - -/* 0x42 - 0x45 Reserved */ - -#define REG_AFE_COARSE_GAIN_CH1 (0x46) -#define REG_AFE_COARSE_GAIN_CH2 (0x47) -#define REG_AFE_COARSE_GAIN_CH3 (0x48) -#define REG_AFE_COARSE_GAIN_CH4 (0x49) - -#define REG_AFE_FINE_GAIN_PB_B_LSB (0x4A) -#define REG_AFE_FINE_GAIN_PB_B_MSB (0x4B) -#define REG_AFE_FINE_GAIN_Y_G_CHROMA_LSB (0x4C) -#define REG_AFE_FINE_GAIN_Y_G_CHROMA_MSB (0x4D) -#define REG_AFE_FINE_GAIN_PR_R_LSB (0x4E) -#define REG_AFE_FINE_GAIN_PR_R_MSB (0x4F) -#define REG_AFE_FINE_GAIN_CVBS_LUMA_LSB (0x50) -#define REG_AFE_FINE_GAIN_CVBS_LUMA_MSB (0x51) - -/* 0x52 - 0x68 Reserved */ - -#define REG_FBIT_VBIT_CONTROL1 (0x69) - -/* 0x6A - 0x6B Reserved */ - -#define REG_BACKEND_AGC_CONTROL (0x6C) - -/* 0x6D - 0x6E Reserved */ - -#define REG_AGC_DECREMENT_SPEED_CONTROL (0x6F) -#define REG_ROM_VERSION (0x70) - -/* 0x71 - 0x73 Reserved */ - -#define REG_AGC_WHITE_PEAK_PROCESSING (0x74) -#define REG_FBIT_VBIT_CONTROL2 (0x75) -#define REG_VCR_TRICK_MODE_CONTROL (0x76) -#define REG_HORIZONTAL_SHAKE_INCREMENT (0x77) -#define REG_AGC_INCREMENT_SPEED (0x78) -#define REG_AGC_INCREMENT_DELAY (0x79) - -/* 0x7A - 0x7F Reserved */ - -#define REG_CHIP_ID_MSB (0x80) -#define REG_CHIP_ID_LSB (0x81) - -/* 0x82 Reserved */ - -#define REG_CPLL_SPEED_CONTROL (0x83) - -/* 0x84 - 0x96 Reserved */ - -#define REG_STATUS_REQUEST (0x97) - -/* 0x98 - 0x99 Reserved */ - -#define REG_VERTICAL_LINE_COUNT_LSB (0x9A) -#define REG_VERTICAL_LINE_COUNT_MSB (0x9B) - -/* 0x9C - 0x9D Reserved */ - -#define REG_AGC_DECREMENT_DELAY (0x9E) - -/* 0x9F - 0xB0 Reserved */ - -#define REG_VDP_TTX_FILTER_1_MASK1 (0xB1) -#define REG_VDP_TTX_FILTER_1_MASK2 (0xB2) -#define REG_VDP_TTX_FILTER_1_MASK3 (0xB3) -#define REG_VDP_TTX_FILTER_1_MASK4 (0xB4) -#define REG_VDP_TTX_FILTER_1_MASK5 (0xB5) -#define REG_VDP_TTX_FILTER_2_MASK1 (0xB6) -#define REG_VDP_TTX_FILTER_2_MASK2 (0xB7) -#define REG_VDP_TTX_FILTER_2_MASK3 (0xB8) -#define REG_VDP_TTX_FILTER_2_MASK4 (0xB9) -#define REG_VDP_TTX_FILTER_2_MASK5 (0xBA) -#define REG_VDP_TTX_FILTER_CONTROL (0xBB) -#define REG_VDP_FIFO_WORD_COUNT (0xBC) -#define REG_VDP_FIFO_INTERRUPT_THRLD (0xBD) - -/* 0xBE Reserved */ - -#define REG_VDP_FIFO_RESET (0xBF) -#define REG_VDP_FIFO_OUTPUT_CONTROL (0xC0) -#define REG_VDP_LINE_NUMBER_INTERRUPT (0xC1) -#define REG_VDP_PIXEL_ALIGNMENT_LSB (0xC2) -#define REG_VDP_PIXEL_ALIGNMENT_MSB (0xC3) - -/* 0xC4 - 0xD5 Reserved */ - -#define REG_VDP_LINE_START (0xD6) -#define REG_VDP_LINE_STOP (0xD7) -#define REG_VDP_GLOBAL_LINE_MODE (0xD8) -#define REG_VDP_FULL_FIELD_ENABLE (0xD9) -#define REG_VDP_FULL_FIELD_MODE (0xDA) - -/* 0xDB - 0xDF Reserved */ - -#define REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR (0xE0) -#define REG_VBUS_DATA_ACCESS_VBUS_ADDR_INCR (0xE1) -#define REG_FIFO_READ_DATA (0xE2) - -/* 0xE3 - 0xE7 Reserved */ - -#define REG_VBUS_ADDRESS_ACCESS1 (0xE8) -#define REG_VBUS_ADDRESS_ACCESS2 (0xE9) -#define REG_VBUS_ADDRESS_ACCESS3 (0xEA) - -/* 0xEB - 0xEF Reserved */ - -#define REG_INTERRUPT_RAW_STATUS0 (0xF0) -#define REG_INTERRUPT_RAW_STATUS1 (0xF1) -#define REG_INTERRUPT_STATUS0 (0xF2) -#define REG_INTERRUPT_STATUS1 (0xF3) -#define REG_INTERRUPT_MASK0 (0xF4) -#define REG_INTERRUPT_MASK1 (0xF5) -#define REG_INTERRUPT_CLEAR0 (0xF6) -#define REG_INTERRUPT_CLEAR1 (0xF7) - -/* 0xF8 - 0xFF Reserved */ - -/* - * Mask and bit definitions of TVP5146/47 registers - */ -/* The ID values we are looking for */ -#define TVP514X_CHIP_ID_MSB (0x51) -#define TVP5146_CHIP_ID_LSB (0x46) -#define TVP5147_CHIP_ID_LSB (0x47) - -#define VIDEO_STD_MASK (0x07) -#define VIDEO_STD_AUTO_SWITCH_BIT (0x00) -#define VIDEO_STD_NTSC_MJ_BIT (0x01) -#define VIDEO_STD_PAL_BDGHIN_BIT (0x02) -#define VIDEO_STD_PAL_M_BIT (0x03) -#define VIDEO_STD_PAL_COMBINATION_N_BIT (0x04) -#define VIDEO_STD_NTSC_4_43_BIT (0x05) -#define VIDEO_STD_SECAM_BIT (0x06) -#define VIDEO_STD_PAL_60_BIT (0x07) - -/* - * Status bit - */ -#define STATUS_TV_VCR_BIT (1<<0) -#define STATUS_HORZ_SYNC_LOCK_BIT (1<<1) -#define STATUS_VIRT_SYNC_LOCK_BIT (1<<2) -#define STATUS_CLR_SUBCAR_LOCK_BIT (1<<3) -#define STATUS_LOST_LOCK_DETECT_BIT (1<<4) -#define STATUS_FEILD_RATE_BIT (1<<5) -#define STATUS_LINE_ALTERNATING_BIT (1<<6) -#define STATUS_PEAK_WHITE_DETECT_BIT (1<<7) - -/* Tokens for register write */ -#define TOK_WRITE (0) /* token for write operation */ -#define TOK_TERM (1) /* terminating token */ -#define TOK_DELAY (2) /* delay token for reg list */ -#define TOK_SKIP (3) /* token to skip a register */ -/** - * struct tvp514x_reg - Structure for TVP5146/47 register initialization values - * @token - Token: TOK_WRITE, TOK_TERM etc.. - * @reg - Register offset - * @val - Register Value for TOK_WRITE or delay in ms for TOK_DELAY - */ -struct tvp514x_reg { - u8 token; - u8 reg; - u32 val; -}; - -#endif /* ifndef _TVP514X_REGS_H */ diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c deleted file mode 100644 index a751b6c146f..00000000000 --- a/drivers/media/video/tvp5150.c +++ /dev/null @@ -1,1274 +0,0 @@ -/* - * tvp5150 - Texas Instruments TVP5150A/AM1 video decoder driver - * - * Copyright (c) 2005,2006 Mauro Carvalho Chehab (mchehab@infradead.org) - * This code is placed under the terms of the GNU General Public License v2 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tvp5150_reg.h" - -#define TVP5150_H_MAX 720 -#define TVP5150_V_MAX_525_60 480 -#define TVP5150_V_MAX_OTHERS 576 -#define TVP5150_MAX_CROP_LEFT 511 -#define TVP5150_MAX_CROP_TOP 127 -#define TVP5150_CROP_SHIFT 2 - -MODULE_DESCRIPTION("Texas Instruments TVP5150A video decoder driver"); -MODULE_AUTHOR("Mauro Carvalho Chehab"); -MODULE_LICENSE("GPL"); - - -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-2)"); - -struct tvp5150 { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - struct v4l2_rect rect; - - v4l2_std_id norm; /* Current set standard */ - u32 input; - u32 output; - int enable; -}; - -static inline struct tvp5150 *to_tvp5150(struct v4l2_subdev *sd) -{ - return container_of(sd, struct tvp5150, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct tvp5150, hdl)->sd; -} - -static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr) -{ - struct i2c_client *c = v4l2_get_subdevdata(sd); - unsigned char buffer[1]; - int rc; - - buffer[0] = addr; - - rc = i2c_master_send(c, buffer, 1); - if (rc < 0) { - v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc); - return rc; - } - - msleep(10); - - rc = i2c_master_recv(c, buffer, 1); - if (rc < 0) { - v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc); - return rc; - } - - v4l2_dbg(2, debug, sd, "tvp5150: read 0x%02x = 0x%02x\n", addr, buffer[0]); - - return (buffer[0]); -} - -static inline void tvp5150_write(struct v4l2_subdev *sd, unsigned char addr, - unsigned char value) -{ - struct i2c_client *c = v4l2_get_subdevdata(sd); - unsigned char buffer[2]; - int rc; - - buffer[0] = addr; - buffer[1] = value; - v4l2_dbg(2, debug, sd, "tvp5150: writing 0x%02x 0x%02x\n", buffer[0], buffer[1]); - if (2 != (rc = i2c_master_send(c, buffer, 2))) - v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 2)\n", rc); -} - -static void dump_reg_range(struct v4l2_subdev *sd, char *s, u8 init, - const u8 end, int max_line) -{ - int i = 0; - - while (init != (u8)(end + 1)) { - if ((i % max_line) == 0) { - if (i > 0) - printk("\n"); - printk("tvp5150: %s reg 0x%02x = ", s, init); - } - printk("%02x ", tvp5150_read(sd, init)); - - init++; - i++; - } - printk("\n"); -} - -static int tvp5150_log_status(struct v4l2_subdev *sd) -{ - printk("tvp5150: Video input source selection #1 = 0x%02x\n", - tvp5150_read(sd, TVP5150_VD_IN_SRC_SEL_1)); - printk("tvp5150: Analog channel controls = 0x%02x\n", - tvp5150_read(sd, TVP5150_ANAL_CHL_CTL)); - printk("tvp5150: Operation mode controls = 0x%02x\n", - tvp5150_read(sd, TVP5150_OP_MODE_CTL)); - printk("tvp5150: Miscellaneous controls = 0x%02x\n", - tvp5150_read(sd, TVP5150_MISC_CTL)); - printk("tvp5150: Autoswitch mask= 0x%02x\n", - tvp5150_read(sd, TVP5150_AUTOSW_MSK)); - printk("tvp5150: Color killer threshold control = 0x%02x\n", - tvp5150_read(sd, TVP5150_COLOR_KIL_THSH_CTL)); - printk("tvp5150: Luminance processing controls #1 #2 and #3 = %02x %02x %02x\n", - tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_1), - tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_2), - tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_3)); - printk("tvp5150: Brightness control = 0x%02x\n", - tvp5150_read(sd, TVP5150_BRIGHT_CTL)); - printk("tvp5150: Color saturation control = 0x%02x\n", - tvp5150_read(sd, TVP5150_SATURATION_CTL)); - printk("tvp5150: Hue control = 0x%02x\n", - tvp5150_read(sd, TVP5150_HUE_CTL)); - printk("tvp5150: Contrast control = 0x%02x\n", - tvp5150_read(sd, TVP5150_CONTRAST_CTL)); - printk("tvp5150: Outputs and data rates select = 0x%02x\n", - tvp5150_read(sd, TVP5150_DATA_RATE_SEL)); - printk("tvp5150: Configuration shared pins = 0x%02x\n", - tvp5150_read(sd, TVP5150_CONF_SHARED_PIN)); - printk("tvp5150: Active video cropping start = 0x%02x%02x\n", - tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_MSB), - tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_LSB)); - printk("tvp5150: Active video cropping stop = 0x%02x%02x\n", - tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_MSB), - tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_LSB)); - printk("tvp5150: Genlock/RTC = 0x%02x\n", - tvp5150_read(sd, TVP5150_GENLOCK)); - printk("tvp5150: Horizontal sync start = 0x%02x\n", - tvp5150_read(sd, TVP5150_HORIZ_SYNC_START)); - printk("tvp5150: Vertical blanking start = 0x%02x\n", - tvp5150_read(sd, TVP5150_VERT_BLANKING_START)); - printk("tvp5150: Vertical blanking stop = 0x%02x\n", - tvp5150_read(sd, TVP5150_VERT_BLANKING_STOP)); - printk("tvp5150: Chrominance processing control #1 and #2 = %02x %02x\n", - tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_1), - tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_2)); - printk("tvp5150: Interrupt reset register B = 0x%02x\n", - tvp5150_read(sd, TVP5150_INT_RESET_REG_B)); - printk("tvp5150: Interrupt enable register B = 0x%02x\n", - tvp5150_read(sd, TVP5150_INT_ENABLE_REG_B)); - printk("tvp5150: Interrupt configuration register B = 0x%02x\n", - tvp5150_read(sd, TVP5150_INTT_CONFIG_REG_B)); - printk("tvp5150: Video standard = 0x%02x\n", - tvp5150_read(sd, TVP5150_VIDEO_STD)); - printk("tvp5150: Chroma gain factor: Cb=0x%02x Cr=0x%02x\n", - tvp5150_read(sd, TVP5150_CB_GAIN_FACT), - tvp5150_read(sd, TVP5150_CR_GAIN_FACTOR)); - printk("tvp5150: Macrovision on counter = 0x%02x\n", - tvp5150_read(sd, TVP5150_MACROVISION_ON_CTR)); - printk("tvp5150: Macrovision off counter = 0x%02x\n", - tvp5150_read(sd, TVP5150_MACROVISION_OFF_CTR)); - printk("tvp5150: ITU-R BT.656.%d timing(TVP5150AM1 only)\n", - (tvp5150_read(sd, TVP5150_REV_SELECT) & 1) ? 3 : 4); - printk("tvp5150: Device ID = %02x%02x\n", - tvp5150_read(sd, TVP5150_MSB_DEV_ID), - tvp5150_read(sd, TVP5150_LSB_DEV_ID)); - printk("tvp5150: ROM version = (hex) %02x.%02x\n", - tvp5150_read(sd, TVP5150_ROM_MAJOR_VER), - tvp5150_read(sd, TVP5150_ROM_MINOR_VER)); - printk("tvp5150: Vertical line count = 0x%02x%02x\n", - tvp5150_read(sd, TVP5150_VERT_LN_COUNT_MSB), - tvp5150_read(sd, TVP5150_VERT_LN_COUNT_LSB)); - printk("tvp5150: Interrupt status register B = 0x%02x\n", - tvp5150_read(sd, TVP5150_INT_STATUS_REG_B)); - printk("tvp5150: Interrupt active register B = 0x%02x\n", - tvp5150_read(sd, TVP5150_INT_ACTIVE_REG_B)); - printk("tvp5150: Status regs #1 to #5 = %02x %02x %02x %02x %02x\n", - tvp5150_read(sd, TVP5150_STATUS_REG_1), - tvp5150_read(sd, TVP5150_STATUS_REG_2), - tvp5150_read(sd, TVP5150_STATUS_REG_3), - tvp5150_read(sd, TVP5150_STATUS_REG_4), - tvp5150_read(sd, TVP5150_STATUS_REG_5)); - - dump_reg_range(sd, "Teletext filter 1", TVP5150_TELETEXT_FIL1_INI, - TVP5150_TELETEXT_FIL1_END, 8); - dump_reg_range(sd, "Teletext filter 2", TVP5150_TELETEXT_FIL2_INI, - TVP5150_TELETEXT_FIL2_END, 8); - - printk("tvp5150: Teletext filter enable = 0x%02x\n", - tvp5150_read(sd, TVP5150_TELETEXT_FIL_ENA)); - printk("tvp5150: Interrupt status register A = 0x%02x\n", - tvp5150_read(sd, TVP5150_INT_STATUS_REG_A)); - printk("tvp5150: Interrupt enable register A = 0x%02x\n", - tvp5150_read(sd, TVP5150_INT_ENABLE_REG_A)); - printk("tvp5150: Interrupt configuration = 0x%02x\n", - tvp5150_read(sd, TVP5150_INT_CONF)); - printk("tvp5150: VDP status register = 0x%02x\n", - tvp5150_read(sd, TVP5150_VDP_STATUS_REG)); - printk("tvp5150: FIFO word count = 0x%02x\n", - tvp5150_read(sd, TVP5150_FIFO_WORD_COUNT)); - printk("tvp5150: FIFO interrupt threshold = 0x%02x\n", - tvp5150_read(sd, TVP5150_FIFO_INT_THRESHOLD)); - printk("tvp5150: FIFO reset = 0x%02x\n", - tvp5150_read(sd, TVP5150_FIFO_RESET)); - printk("tvp5150: Line number interrupt = 0x%02x\n", - tvp5150_read(sd, TVP5150_LINE_NUMBER_INT)); - printk("tvp5150: Pixel alignment register = 0x%02x%02x\n", - tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_HIGH), - tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_LOW)); - printk("tvp5150: FIFO output control = 0x%02x\n", - tvp5150_read(sd, TVP5150_FIFO_OUT_CTRL)); - printk("tvp5150: Full field enable = 0x%02x\n", - tvp5150_read(sd, TVP5150_FULL_FIELD_ENA)); - printk("tvp5150: Full field mode register = 0x%02x\n", - tvp5150_read(sd, TVP5150_FULL_FIELD_MODE_REG)); - - dump_reg_range(sd, "CC data", TVP5150_CC_DATA_INI, - TVP5150_CC_DATA_END, 8); - - dump_reg_range(sd, "WSS data", TVP5150_WSS_DATA_INI, - TVP5150_WSS_DATA_END, 8); - - dump_reg_range(sd, "VPS data", TVP5150_VPS_DATA_INI, - TVP5150_VPS_DATA_END, 8); - - dump_reg_range(sd, "VITC data", TVP5150_VITC_DATA_INI, - TVP5150_VITC_DATA_END, 10); - - dump_reg_range(sd, "Line mode", TVP5150_LINE_MODE_INI, - TVP5150_LINE_MODE_END, 8); - return 0; -} - -/**************************************************************************** - Basic functions - ****************************************************************************/ - -static inline void tvp5150_selmux(struct v4l2_subdev *sd) -{ - int opmode = 0; - struct tvp5150 *decoder = to_tvp5150(sd); - int input = 0; - int val; - - if ((decoder->output & TVP5150_BLACK_SCREEN) || !decoder->enable) - input = 8; - - switch (decoder->input) { - case TVP5150_COMPOSITE1: - input |= 2; - /* fall through */ - case TVP5150_COMPOSITE0: - break; - case TVP5150_SVIDEO: - default: - input |= 1; - break; - } - - v4l2_dbg(1, debug, sd, "Selecting video route: route input=%i, output=%i " - "=> tvp5150 input=%i, opmode=%i\n", - decoder->input, decoder->output, - input, opmode); - - tvp5150_write(sd, TVP5150_OP_MODE_CTL, opmode); - tvp5150_write(sd, TVP5150_VD_IN_SRC_SEL_1, input); - - /* Svideo should enable YCrCb output and disable GPCL output - * For Composite and TV, it should be the reverse - */ - val = tvp5150_read(sd, TVP5150_MISC_CTL); - if (val < 0) { - v4l2_err(sd, "%s: failed with error = %d\n", __func__, val); - return; - } - - if (decoder->input == TVP5150_SVIDEO) - val = (val & ~0x40) | 0x10; - else - val = (val & ~0x10) | 0x40; - tvp5150_write(sd, TVP5150_MISC_CTL, val); -}; - -struct i2c_reg_value { - unsigned char reg; - unsigned char value; -}; - -/* Default values as sugested at TVP5150AM1 datasheet */ -static const struct i2c_reg_value tvp5150_init_default[] = { - { /* 0x00 */ - TVP5150_VD_IN_SRC_SEL_1,0x00 - }, - { /* 0x01 */ - TVP5150_ANAL_CHL_CTL,0x15 - }, - { /* 0x02 */ - TVP5150_OP_MODE_CTL,0x00 - }, - { /* 0x03 */ - TVP5150_MISC_CTL,0x01 - }, - { /* 0x06 */ - TVP5150_COLOR_KIL_THSH_CTL,0x10 - }, - { /* 0x07 */ - TVP5150_LUMA_PROC_CTL_1,0x60 - }, - { /* 0x08 */ - TVP5150_LUMA_PROC_CTL_2,0x00 - }, - { /* 0x09 */ - TVP5150_BRIGHT_CTL,0x80 - }, - { /* 0x0a */ - TVP5150_SATURATION_CTL,0x80 - }, - { /* 0x0b */ - TVP5150_HUE_CTL,0x00 - }, - { /* 0x0c */ - TVP5150_CONTRAST_CTL,0x80 - }, - { /* 0x0d */ - TVP5150_DATA_RATE_SEL,0x47 - }, - { /* 0x0e */ - TVP5150_LUMA_PROC_CTL_3,0x00 - }, - { /* 0x0f */ - TVP5150_CONF_SHARED_PIN,0x08 - }, - { /* 0x11 */ - TVP5150_ACT_VD_CROP_ST_MSB,0x00 - }, - { /* 0x12 */ - TVP5150_ACT_VD_CROP_ST_LSB,0x00 - }, - { /* 0x13 */ - TVP5150_ACT_VD_CROP_STP_MSB,0x00 - }, - { /* 0x14 */ - TVP5150_ACT_VD_CROP_STP_LSB,0x00 - }, - { /* 0x15 */ - TVP5150_GENLOCK,0x01 - }, - { /* 0x16 */ - TVP5150_HORIZ_SYNC_START,0x80 - }, - { /* 0x18 */ - TVP5150_VERT_BLANKING_START,0x00 - }, - { /* 0x19 */ - TVP5150_VERT_BLANKING_STOP,0x00 - }, - { /* 0x1a */ - TVP5150_CHROMA_PROC_CTL_1,0x0c - }, - { /* 0x1b */ - TVP5150_CHROMA_PROC_CTL_2,0x14 - }, - { /* 0x1c */ - TVP5150_INT_RESET_REG_B,0x00 - }, - { /* 0x1d */ - TVP5150_INT_ENABLE_REG_B,0x00 - }, - { /* 0x1e */ - TVP5150_INTT_CONFIG_REG_B,0x00 - }, - { /* 0x28 */ - TVP5150_VIDEO_STD,0x00 - }, - { /* 0x2e */ - TVP5150_MACROVISION_ON_CTR,0x0f - }, - { /* 0x2f */ - TVP5150_MACROVISION_OFF_CTR,0x01 - }, - { /* 0xbb */ - TVP5150_TELETEXT_FIL_ENA,0x00 - }, - { /* 0xc0 */ - TVP5150_INT_STATUS_REG_A,0x00 - }, - { /* 0xc1 */ - TVP5150_INT_ENABLE_REG_A,0x00 - }, - { /* 0xc2 */ - TVP5150_INT_CONF,0x04 - }, - { /* 0xc8 */ - TVP5150_FIFO_INT_THRESHOLD,0x80 - }, - { /* 0xc9 */ - TVP5150_FIFO_RESET,0x00 - }, - { /* 0xca */ - TVP5150_LINE_NUMBER_INT,0x00 - }, - { /* 0xcb */ - TVP5150_PIX_ALIGN_REG_LOW,0x4e - }, - { /* 0xcc */ - TVP5150_PIX_ALIGN_REG_HIGH,0x00 - }, - { /* 0xcd */ - TVP5150_FIFO_OUT_CTRL,0x01 - }, - { /* 0xcf */ - TVP5150_FULL_FIELD_ENA,0x00 - }, - { /* 0xd0 */ - TVP5150_LINE_MODE_INI,0x00 - }, - { /* 0xfc */ - TVP5150_FULL_FIELD_MODE_REG,0x7f - }, - { /* end of data */ - 0xff,0xff - } -}; - -/* Default values as sugested at TVP5150AM1 datasheet */ -static const struct i2c_reg_value tvp5150_init_enable[] = { - { - TVP5150_CONF_SHARED_PIN, 2 - },{ /* Automatic offset and AGC enabled */ - TVP5150_ANAL_CHL_CTL, 0x15 - },{ /* Activate YCrCb output 0x9 or 0xd ? */ - TVP5150_MISC_CTL, 0x6f - },{ /* Activates video std autodetection for all standards */ - TVP5150_AUTOSW_MSK, 0x0 - },{ /* Default format: 0x47. For 4:2:2: 0x40 */ - TVP5150_DATA_RATE_SEL, 0x47 - },{ - TVP5150_CHROMA_PROC_CTL_1, 0x0c - },{ - TVP5150_CHROMA_PROC_CTL_2, 0x54 - },{ /* Non documented, but initialized on WinTV USB2 */ - 0x27, 0x20 - },{ - 0xff,0xff - } -}; - -struct tvp5150_vbi_type { - unsigned int vbi_type; - unsigned int ini_line; - unsigned int end_line; - unsigned int by_field :1; -}; - -struct i2c_vbi_ram_value { - u16 reg; - struct tvp5150_vbi_type type; - unsigned char values[16]; -}; - -/* This struct have the values for each supported VBI Standard - * by - tvp5150_vbi_types should follow the same order as vbi_ram_default - * value 0 means rom position 0x10, value 1 means rom position 0x30 - * and so on. There are 16 possible locations from 0 to 15. - */ - -static struct i2c_vbi_ram_value vbi_ram_default[] = -{ - /* FIXME: Current api doesn't handle all VBI types, those not - yet supported are placed under #if 0 */ -#if 0 - {0x010, /* Teletext, SECAM, WST System A */ - {V4L2_SLICED_TELETEXT_SECAM,6,23,1}, - { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x26, - 0xe6, 0xb4, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00 } - }, -#endif - {0x030, /* Teletext, PAL, WST System B */ - {V4L2_SLICED_TELETEXT_B,6,22,1}, - { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x2b, - 0xa6, 0x72, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00 } - }, -#if 0 - {0x050, /* Teletext, PAL, WST System C */ - {V4L2_SLICED_TELETEXT_PAL_C,6,22,1}, - { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22, - 0xa6, 0x98, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } - }, - {0x070, /* Teletext, NTSC, WST System B */ - {V4L2_SLICED_TELETEXT_NTSC_B,10,21,1}, - { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x23, - 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } - }, - {0x090, /* Tetetext, NTSC NABTS System C */ - {V4L2_SLICED_TELETEXT_NTSC_C,10,21,1}, - { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22, - 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x15, 0x00 } - }, - {0x0b0, /* Teletext, NTSC-J, NABTS System D */ - {V4L2_SLICED_TELETEXT_NTSC_D,10,21,1}, - { 0xaa, 0xaa, 0xff, 0xff, 0xa7, 0x2e, 0x20, 0x23, - 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } - }, - {0x0d0, /* Closed Caption, PAL/SECAM */ - {V4L2_SLICED_CAPTION_625,22,22,1}, - { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02, - 0xa6, 0x7b, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 } - }, -#endif - {0x0f0, /* Closed Caption, NTSC */ - {V4L2_SLICED_CAPTION_525,21,21,1}, - { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02, - 0x69, 0x8c, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 } - }, - {0x110, /* Wide Screen Signal, PAL/SECAM */ - {V4L2_SLICED_WSS_625,23,23,1}, - { 0x5b, 0x55, 0xc5, 0xff, 0x00, 0x71, 0x6e, 0x42, - 0xa6, 0xcd, 0x0f, 0x00, 0x00, 0x00, 0x3a, 0x00 } - }, -#if 0 - {0x130, /* Wide Screen Signal, NTSC C */ - {V4L2_SLICED_WSS_525,20,20,1}, - { 0x38, 0x00, 0x3f, 0x00, 0x00, 0x71, 0x6e, 0x43, - 0x69, 0x7c, 0x08, 0x00, 0x00, 0x00, 0x39, 0x00 } - }, - {0x150, /* Vertical Interval Timecode (VITC), PAL/SECAM */ - {V4l2_SLICED_VITC_625,6,22,0}, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49, - 0xa6, 0x85, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 } - }, - {0x170, /* Vertical Interval Timecode (VITC), NTSC */ - {V4l2_SLICED_VITC_525,10,20,0}, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49, - 0x69, 0x94, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 } - }, -#endif - {0x190, /* Video Program System (VPS), PAL */ - {V4L2_SLICED_VPS,16,16,0}, - { 0xaa, 0xaa, 0xff, 0xff, 0xba, 0xce, 0x2b, 0x0d, - 0xa6, 0xda, 0x0b, 0x00, 0x00, 0x00, 0x60, 0x00 } - }, - /* 0x1d0 User programmable */ - - /* End of struct */ - { (u16)-1 } -}; - -static int tvp5150_write_inittab(struct v4l2_subdev *sd, - const struct i2c_reg_value *regs) -{ - while (regs->reg != 0xff) { - tvp5150_write(sd, regs->reg, regs->value); - regs++; - } - return 0; -} - -static int tvp5150_vdp_init(struct v4l2_subdev *sd, - const struct i2c_vbi_ram_value *regs) -{ - unsigned int i; - - /* Disable Full Field */ - tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0); - - /* Before programming, Line mode should be at 0xff */ - for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++) - tvp5150_write(sd, i, 0xff); - - /* Load Ram Table */ - while (regs->reg != (u16)-1) { - tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_HIGH, regs->reg >> 8); - tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_LOW, regs->reg); - - for (i = 0; i < 16; i++) - tvp5150_write(sd, TVP5150_VDP_CONF_RAM_DATA, regs->values[i]); - - regs++; - } - return 0; -} - -/* Fills VBI capabilities based on i2c_vbi_ram_value struct */ -static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd, - struct v4l2_sliced_vbi_cap *cap) -{ - const struct i2c_vbi_ram_value *regs = vbi_ram_default; - int line; - - v4l2_dbg(1, debug, sd, "g_sliced_vbi_cap\n"); - memset(cap, 0, sizeof *cap); - - while (regs->reg != (u16)-1 ) { - for (line=regs->type.ini_line;line<=regs->type.end_line;line++) { - cap->service_lines[0][line] |= regs->type.vbi_type; - } - cap->service_set |= regs->type.vbi_type; - - regs++; - } - return 0; -} - -/* Set vbi processing - * type - one of tvp5150_vbi_types - * line - line to gather data - * fields: bit 0 field1, bit 1, field2 - * flags (default=0xf0) is a bitmask, were set means: - * bit 7: enable filtering null bytes on CC - * bit 6: send data also to FIFO - * bit 5: don't allow data with errors on FIFO - * bit 4: enable ECC when possible - * pix_align = pix alignment: - * LSB = field1 - * MSB = field2 - */ -static int tvp5150_set_vbi(struct v4l2_subdev *sd, - const struct i2c_vbi_ram_value *regs, - unsigned int type,u8 flags, int line, - const int fields) -{ - struct tvp5150 *decoder = to_tvp5150(sd); - v4l2_std_id std = decoder->norm; - u8 reg; - int pos=0; - - if (std == V4L2_STD_ALL) { - v4l2_err(sd, "VBI can't be configured without knowing number of lines\n"); - return 0; - } else if (std & V4L2_STD_625_50) { - /* Don't follow NTSC Line number convension */ - line += 3; - } - - if (line<6||line>27) - return 0; - - while (regs->reg != (u16)-1 ) { - if ((type & regs->type.vbi_type) && - (line>=regs->type.ini_line) && - (line<=regs->type.end_line)) { - type=regs->type.vbi_type; - break; - } - - regs++; - pos++; - } - if (regs->reg == (u16)-1) - return 0; - - type=pos | (flags & 0xf0); - reg=((line-6)<<1)+TVP5150_LINE_MODE_INI; - - if (fields&1) { - tvp5150_write(sd, reg, type); - } - - if (fields&2) { - tvp5150_write(sd, reg+1, type); - } - - return type; -} - -static int tvp5150_get_vbi(struct v4l2_subdev *sd, - const struct i2c_vbi_ram_value *regs, int line) -{ - struct tvp5150 *decoder = to_tvp5150(sd); - v4l2_std_id std = decoder->norm; - u8 reg; - int pos, type = 0; - int i, ret = 0; - - if (std == V4L2_STD_ALL) { - v4l2_err(sd, "VBI can't be configured without knowing number of lines\n"); - return 0; - } else if (std & V4L2_STD_625_50) { - /* Don't follow NTSC Line number convension */ - line += 3; - } - - if (line < 6 || line > 27) - return 0; - - reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI; - - for (i = 0; i <= 1; i++) { - ret = tvp5150_read(sd, reg + i); - if (ret < 0) { - v4l2_err(sd, "%s: failed with error = %d\n", - __func__, ret); - return 0; - } - pos = ret & 0x0f; - if (pos < 0x0f) - type |= regs[pos].type.vbi_type; - } - - return type; -} - -static int tvp5150_set_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct tvp5150 *decoder = to_tvp5150(sd); - int fmt = 0; - - decoder->norm = std; - - /* First tests should be against specific std */ - - if (std == V4L2_STD_ALL) { - fmt = VIDEO_STD_AUTO_SWITCH_BIT; /* Autodetect mode */ - } else if (std & V4L2_STD_NTSC_443) { - fmt = VIDEO_STD_NTSC_4_43_BIT; - } else if (std & V4L2_STD_PAL_M) { - fmt = VIDEO_STD_PAL_M_BIT; - } else if (std & (V4L2_STD_PAL_N | V4L2_STD_PAL_Nc)) { - fmt = VIDEO_STD_PAL_COMBINATION_N_BIT; - } else { - /* Then, test against generic ones */ - if (std & V4L2_STD_NTSC) - fmt = VIDEO_STD_NTSC_MJ_BIT; - else if (std & V4L2_STD_PAL) - fmt = VIDEO_STD_PAL_BDGHIN_BIT; - else if (std & V4L2_STD_SECAM) - fmt = VIDEO_STD_SECAM_BIT; - } - - v4l2_dbg(1, debug, sd, "Set video std register to %d.\n", fmt); - tvp5150_write(sd, TVP5150_VIDEO_STD, fmt); - return 0; -} - -static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct tvp5150 *decoder = to_tvp5150(sd); - - if (decoder->norm == std) - return 0; - - /* Change cropping height limits */ - if (std & V4L2_STD_525_60) - decoder->rect.height = TVP5150_V_MAX_525_60; - else - decoder->rect.height = TVP5150_V_MAX_OTHERS; - - - return tvp5150_set_std(sd, std); -} - -static int tvp5150_reset(struct v4l2_subdev *sd, u32 val) -{ - struct tvp5150 *decoder = to_tvp5150(sd); - - /* Initializes TVP5150 to its default values */ - tvp5150_write_inittab(sd, tvp5150_init_default); - - /* Initializes VDP registers */ - tvp5150_vdp_init(sd, vbi_ram_default); - - /* Selects decoder input */ - tvp5150_selmux(sd); - - /* Initializes TVP5150 to stream enabled values */ - tvp5150_write_inittab(sd, tvp5150_init_enable); - - /* Initialize image preferences */ - v4l2_ctrl_handler_setup(&decoder->hdl); - - tvp5150_set_std(sd, decoder->norm); - return 0; -}; - -static int tvp5150_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - tvp5150_write(sd, TVP5150_BRIGHT_CTL, ctrl->val); - return 0; - case V4L2_CID_CONTRAST: - tvp5150_write(sd, TVP5150_CONTRAST_CTL, ctrl->val); - return 0; - case V4L2_CID_SATURATION: - tvp5150_write(sd, TVP5150_SATURATION_CTL, ctrl->val); - return 0; - case V4L2_CID_HUE: - tvp5150_write(sd, TVP5150_HUE_CTL, ctrl->val); - return 0; - } - return -EINVAL; -} - -static v4l2_std_id tvp5150_read_std(struct v4l2_subdev *sd) -{ - int val = tvp5150_read(sd, TVP5150_STATUS_REG_5); - - switch (val & 0x0F) { - case 0x01: - return V4L2_STD_NTSC; - case 0x03: - return V4L2_STD_PAL; - case 0x05: - return V4L2_STD_PAL_M; - case 0x07: - return V4L2_STD_PAL_N | V4L2_STD_PAL_Nc; - case 0x09: - return V4L2_STD_NTSC_443; - case 0xb: - return V4L2_STD_SECAM; - default: - return V4L2_STD_UNKNOWN; - } -} - -static int tvp5150_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, - enum v4l2_mbus_pixelcode *code) -{ - if (index) - return -EINVAL; - - *code = V4L2_MBUS_FMT_UYVY8_2X8; - return 0; -} - -static int tvp5150_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *f) -{ - struct tvp5150 *decoder = to_tvp5150(sd); - - if (f == NULL) - return -EINVAL; - - tvp5150_reset(sd, 0); - - f->width = decoder->rect.width; - f->height = decoder->rect.height; - - f->code = V4L2_MBUS_FMT_UYVY8_2X8; - f->field = V4L2_FIELD_SEQ_TB; - f->colorspace = V4L2_COLORSPACE_SMPTE170M; - - v4l2_dbg(1, debug, sd, "width = %d, height = %d\n", f->width, - f->height); - return 0; -} - -static int tvp5150_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) -{ - struct v4l2_rect rect = a->c; - struct tvp5150 *decoder = to_tvp5150(sd); - v4l2_std_id std; - int hmax; - - v4l2_dbg(1, debug, sd, "%s left=%d, top=%d, width=%d, height=%d\n", - __func__, rect.left, rect.top, rect.width, rect.height); - - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - /* tvp5150 has some special limits */ - rect.left = clamp(rect.left, 0, TVP5150_MAX_CROP_LEFT); - rect.width = clamp(rect.width, - TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left, - TVP5150_H_MAX - rect.left); - rect.top = clamp(rect.top, 0, TVP5150_MAX_CROP_TOP); - - /* Calculate height based on current standard */ - if (decoder->norm == V4L2_STD_ALL) - std = tvp5150_read_std(sd); - else - std = decoder->norm; - - if (std & V4L2_STD_525_60) - hmax = TVP5150_V_MAX_525_60; - else - hmax = TVP5150_V_MAX_OTHERS; - - rect.height = clamp(rect.height, - hmax - TVP5150_MAX_CROP_TOP - rect.top, - hmax - rect.top); - - tvp5150_write(sd, TVP5150_VERT_BLANKING_START, rect.top); - tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, - rect.top + rect.height - hmax); - tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_MSB, - rect.left >> TVP5150_CROP_SHIFT); - tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_LSB, - rect.left | (1 << TVP5150_CROP_SHIFT)); - tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_MSB, - (rect.left + rect.width - TVP5150_MAX_CROP_LEFT) >> - TVP5150_CROP_SHIFT); - tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_LSB, - rect.left + rect.width - TVP5150_MAX_CROP_LEFT); - - decoder->rect = rect; - - return 0; -} - -static int tvp5150_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) -{ - struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd); - - a->c = decoder->rect; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - return 0; -} - -static int tvp5150_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) -{ - struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd); - v4l2_std_id std; - - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - a->bounds.left = 0; - a->bounds.top = 0; - a->bounds.width = TVP5150_H_MAX; - - /* Calculate height based on current standard */ - if (decoder->norm == V4L2_STD_ALL) - std = tvp5150_read_std(sd); - else - std = decoder->norm; - - if (std & V4L2_STD_525_60) - a->bounds.height = TVP5150_V_MAX_525_60; - else - a->bounds.height = TVP5150_V_MAX_OTHERS; - - a->defrect = a->bounds; - a->pixelaspect.numerator = 1; - a->pixelaspect.denominator = 1; - - return 0; -} - -/**************************************************************************** - I2C Command - ****************************************************************************/ - -static int tvp5150_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct tvp5150 *decoder = to_tvp5150(sd); - - decoder->input = input; - decoder->output = output; - tvp5150_selmux(sd); - return 0; -} - -static int tvp5150_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) -{ - /* this is for capturing 36 raw vbi lines - if there's a way to cut off the beginning 2 vbi lines - with the tvp5150 then the vbi line count could be lowered - to 17 lines/field again, although I couldn't find a register - which could do that cropping */ - if (fmt->sample_format == V4L2_PIX_FMT_GREY) - tvp5150_write(sd, TVP5150_LUMA_PROC_CTL_1, 0x70); - if (fmt->count[0] == 18 && fmt->count[1] == 18) { - tvp5150_write(sd, TVP5150_VERT_BLANKING_START, 0x00); - tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, 0x01); - } - return 0; -} - -static int tvp5150_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) -{ - int i; - - if (svbi->service_set != 0) { - for (i = 0; i <= 23; i++) { - svbi->service_lines[1][i] = 0; - svbi->service_lines[0][i] = - tvp5150_set_vbi(sd, vbi_ram_default, - svbi->service_lines[0][i], 0xf0, i, 3); - } - /* Enables FIFO */ - tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 1); - } else { - /* Disables FIFO*/ - tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 0); - - /* Disable Full Field */ - tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0); - - /* Disable Line modes */ - for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++) - tvp5150_write(sd, i, 0xff); - } - return 0; -} - -static int tvp5150_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) -{ - int i, mask = 0; - - memset(svbi, 0, sizeof(*svbi)); - - for (i = 0; i <= 23; i++) { - svbi->service_lines[0][i] = - tvp5150_get_vbi(sd, vbi_ram_default, i); - mask |= svbi->service_lines[0][i]; - } - svbi->service_set = mask; - return 0; -} - -static int tvp5150_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *chip) -{ - int rev; - struct i2c_client *client = v4l2_get_subdevdata(sd); - - rev = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER) << 8 | - tvp5150_read(sd, TVP5150_ROM_MINOR_VER); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP5150, - rev); -} - - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - int res; - - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - res = tvp5150_read(sd, reg->reg & 0xff); - if (res < 0) { - v4l2_err(sd, "%s: failed with error = %d\n", __func__, res); - return res; - } - - reg->val = res; - reg->size = 1; - return 0; -} - -static int tvp5150_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - tvp5150_write(sd, reg->reg & 0xff, reg->val & 0xff); - return 0; -} -#endif - -static int tvp5150_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - int status = tvp5150_read(sd, 0x88); - - vt->signal = ((status & 0x04) && (status & 0x02)) ? 0xffff : 0x0; - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = { - .s_ctrl = tvp5150_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops tvp5150_core_ops = { - .log_status = tvp5150_log_status, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, - .s_std = tvp5150_s_std, - .reset = tvp5150_reset, - .g_chip_ident = tvp5150_g_chip_ident, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = tvp5150_g_register, - .s_register = tvp5150_s_register, -#endif -}; - -static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = { - .g_tuner = tvp5150_g_tuner, -}; - -static const struct v4l2_subdev_video_ops tvp5150_video_ops = { - .s_routing = tvp5150_s_routing, - .enum_mbus_fmt = tvp5150_enum_mbus_fmt, - .s_mbus_fmt = tvp5150_mbus_fmt, - .try_mbus_fmt = tvp5150_mbus_fmt, - .g_mbus_fmt = tvp5150_mbus_fmt, - .s_crop = tvp5150_s_crop, - .g_crop = tvp5150_g_crop, - .cropcap = tvp5150_cropcap, -}; - -static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = { - .g_sliced_vbi_cap = tvp5150_g_sliced_vbi_cap, - .g_sliced_fmt = tvp5150_g_sliced_fmt, - .s_sliced_fmt = tvp5150_s_sliced_fmt, - .s_raw_fmt = tvp5150_s_raw_fmt, -}; - -static const struct v4l2_subdev_ops tvp5150_ops = { - .core = &tvp5150_core_ops, - .tuner = &tvp5150_tuner_ops, - .video = &tvp5150_video_ops, - .vbi = &tvp5150_vbi_ops, -}; - - -/**************************************************************************** - I2C Client & Driver - ****************************************************************************/ - -static int tvp5150_probe(struct i2c_client *c, - const struct i2c_device_id *id) -{ - struct tvp5150 *core; - struct v4l2_subdev *sd; - int tvp5150_id[4]; - int i, res; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(c->adapter, - I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) - return -EIO; - - core = kzalloc(sizeof(struct tvp5150), GFP_KERNEL); - if (!core) { - return -ENOMEM; - } - sd = &core->sd; - v4l2_i2c_subdev_init(sd, c, &tvp5150_ops); - - /* - * Read consequent registers - TVP5150_MSB_DEV_ID, TVP5150_LSB_DEV_ID, - * TVP5150_ROM_MAJOR_VER, TVP5150_ROM_MINOR_VER - */ - for (i = 0; i < 4; i++) { - res = tvp5150_read(sd, TVP5150_MSB_DEV_ID + i); - if (res < 0) - goto free_core; - tvp5150_id[i] = res; - } - - v4l_info(c, "chip found @ 0x%02x (%s)\n", - c->addr << 1, c->adapter->name); - - if (tvp5150_id[2] == 4 && tvp5150_id[3] == 0) { /* Is TVP5150AM1 */ - v4l2_info(sd, "tvp%02x%02xam1 detected.\n", - tvp5150_id[0], tvp5150_id[1]); - - /* ITU-T BT.656.4 timing */ - tvp5150_write(sd, TVP5150_REV_SELECT, 0); - } else { - /* Is TVP5150A */ - if (tvp5150_id[2] == 3 || tvp5150_id[3] == 0x21) { - v4l2_info(sd, "tvp%02x%02xa detected.\n", - tvp5150_id[2], tvp5150_id[3]); - } else { - v4l2_info(sd, "*** unknown tvp%02x%02x chip detected.\n", - tvp5150_id[2], tvp5150_id[3]); - v4l2_info(sd, "*** Rom ver is %d.%d\n", - tvp5150_id[2], tvp5150_id[3]); - } - } - - core->norm = V4L2_STD_ALL; /* Default is autodetect */ - core->input = TVP5150_COMPOSITE1; - core->enable = 1; - - v4l2_ctrl_handler_init(&core->hdl, 4); - v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); - v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, 128); - v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, - V4L2_CID_SATURATION, 0, 255, 1, 128); - v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, - V4L2_CID_HUE, -128, 127, 1, 0); - sd->ctrl_handler = &core->hdl; - if (core->hdl.error) { - res = core->hdl.error; - v4l2_ctrl_handler_free(&core->hdl); - goto free_core; - } - v4l2_ctrl_handler_setup(&core->hdl); - - /* Default is no cropping */ - core->rect.top = 0; - if (tvp5150_read_std(sd) & V4L2_STD_525_60) - core->rect.height = TVP5150_V_MAX_525_60; - else - core->rect.height = TVP5150_V_MAX_OTHERS; - core->rect.left = 0; - core->rect.width = TVP5150_H_MAX; - - if (debug > 1) - tvp5150_log_status(sd); - return 0; - -free_core: - kfree(core); - return res; -} - -static int tvp5150_remove(struct i2c_client *c) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(c); - struct tvp5150 *decoder = to_tvp5150(sd); - - v4l2_dbg(1, debug, sd, - "tvp5150.c: removing tvp5150 adapter on address 0x%x\n", - c->addr << 1); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&decoder->hdl); - kfree(to_tvp5150(sd)); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id tvp5150_id[] = { - { "tvp5150", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tvp5150_id); - -static struct i2c_driver tvp5150_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "tvp5150", - }, - .probe = tvp5150_probe, - .remove = tvp5150_remove, - .id_table = tvp5150_id, -}; - -module_i2c_driver(tvp5150_driver); diff --git a/drivers/media/video/tvp5150_reg.h b/drivers/media/video/tvp5150_reg.h deleted file mode 100644 index 25a99494491..00000000000 --- a/drivers/media/video/tvp5150_reg.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * tvp5150 - Texas Instruments TVP5150A/AM1 video decoder registers - * - * Copyright (c) 2005,2006 Mauro Carvalho Chehab (mchehab@infradead.org) - * This code is placed under the terms of the GNU General Public License v2 - */ - -#define TVP5150_VD_IN_SRC_SEL_1 0x00 /* Video input source selection #1 */ -#define TVP5150_ANAL_CHL_CTL 0x01 /* Analog channel controls */ -#define TVP5150_OP_MODE_CTL 0x02 /* Operation mode controls */ -#define TVP5150_MISC_CTL 0x03 /* Miscellaneous controls */ -#define TVP5150_AUTOSW_MSK 0x04 /* Autoswitch mask: TVP5150A / TVP5150AM */ - -/* Reserved 05h */ - -#define TVP5150_COLOR_KIL_THSH_CTL 0x06 /* Color killer threshold control */ -#define TVP5150_LUMA_PROC_CTL_1 0x07 /* Luminance processing control #1 */ -#define TVP5150_LUMA_PROC_CTL_2 0x08 /* Luminance processing control #2 */ -#define TVP5150_BRIGHT_CTL 0x09 /* Brightness control */ -#define TVP5150_SATURATION_CTL 0x0a /* Color saturation control */ -#define TVP5150_HUE_CTL 0x0b /* Hue control */ -#define TVP5150_CONTRAST_CTL 0x0c /* Contrast control */ -#define TVP5150_DATA_RATE_SEL 0x0d /* Outputs and data rates select */ -#define TVP5150_LUMA_PROC_CTL_3 0x0e /* Luminance processing control #3 */ -#define TVP5150_CONF_SHARED_PIN 0x0f /* Configuration shared pins */ - -/* Reserved 10h */ - -#define TVP5150_ACT_VD_CROP_ST_MSB 0x11 /* Active video cropping start MSB */ -#define TVP5150_ACT_VD_CROP_ST_LSB 0x12 /* Active video cropping start LSB */ -#define TVP5150_ACT_VD_CROP_STP_MSB 0x13 /* Active video cropping stop MSB */ -#define TVP5150_ACT_VD_CROP_STP_LSB 0x14 /* Active video cropping stop LSB */ -#define TVP5150_GENLOCK 0x15 /* Genlock/RTC */ -#define TVP5150_HORIZ_SYNC_START 0x16 /* Horizontal sync start */ - -/* Reserved 17h */ - -#define TVP5150_VERT_BLANKING_START 0x18 /* Vertical blanking start */ -#define TVP5150_VERT_BLANKING_STOP 0x19 /* Vertical blanking stop */ -#define TVP5150_CHROMA_PROC_CTL_1 0x1a /* Chrominance processing control #1 */ -#define TVP5150_CHROMA_PROC_CTL_2 0x1b /* Chrominance processing control #2 */ -#define TVP5150_INT_RESET_REG_B 0x1c /* Interrupt reset register B */ -#define TVP5150_INT_ENABLE_REG_B 0x1d /* Interrupt enable register B */ -#define TVP5150_INTT_CONFIG_REG_B 0x1e /* Interrupt configuration register B */ - -/* Reserved 1Fh-27h */ - -#define VIDEO_STD_MASK (0x07 >> 1) -#define TVP5150_VIDEO_STD 0x28 /* Video standard */ -#define VIDEO_STD_AUTO_SWITCH_BIT 0x00 -#define VIDEO_STD_NTSC_MJ_BIT 0x02 -#define VIDEO_STD_PAL_BDGHIN_BIT 0x04 -#define VIDEO_STD_PAL_M_BIT 0x06 -#define VIDEO_STD_PAL_COMBINATION_N_BIT 0x08 -#define VIDEO_STD_NTSC_4_43_BIT 0x0a -#define VIDEO_STD_SECAM_BIT 0x0c - -#define VIDEO_STD_NTSC_MJ_BIT_AS 0x01 -#define VIDEO_STD_PAL_BDGHIN_BIT_AS 0x03 -#define VIDEO_STD_PAL_M_BIT_AS 0x05 -#define VIDEO_STD_PAL_COMBINATION_N_BIT_AS 0x07 -#define VIDEO_STD_NTSC_4_43_BIT_AS 0x09 -#define VIDEO_STD_SECAM_BIT_AS 0x0b - -/* Reserved 29h-2bh */ - -#define TVP5150_CB_GAIN_FACT 0x2c /* Cb gain factor */ -#define TVP5150_CR_GAIN_FACTOR 0x2d /* Cr gain factor */ -#define TVP5150_MACROVISION_ON_CTR 0x2e /* Macrovision on counter */ -#define TVP5150_MACROVISION_OFF_CTR 0x2f /* Macrovision off counter */ -#define TVP5150_REV_SELECT 0x30 /* revision select (TVP5150AM1 only) */ - -/* Reserved 31h-7Fh */ - -#define TVP5150_MSB_DEV_ID 0x80 /* MSB of device ID */ -#define TVP5150_LSB_DEV_ID 0x81 /* LSB of device ID */ -#define TVP5150_ROM_MAJOR_VER 0x82 /* ROM major version */ -#define TVP5150_ROM_MINOR_VER 0x83 /* ROM minor version */ -#define TVP5150_VERT_LN_COUNT_MSB 0x84 /* Vertical line count MSB */ -#define TVP5150_VERT_LN_COUNT_LSB 0x85 /* Vertical line count LSB */ -#define TVP5150_INT_STATUS_REG_B 0x86 /* Interrupt status register B */ -#define TVP5150_INT_ACTIVE_REG_B 0x87 /* Interrupt active register B */ -#define TVP5150_STATUS_REG_1 0x88 /* Status register #1 */ -#define TVP5150_STATUS_REG_2 0x89 /* Status register #2 */ -#define TVP5150_STATUS_REG_3 0x8a /* Status register #3 */ -#define TVP5150_STATUS_REG_4 0x8b /* Status register #4 */ -#define TVP5150_STATUS_REG_5 0x8c /* Status register #5 */ -/* Reserved 8Dh-8Fh */ - /* Closed caption data registers */ -#define TVP5150_CC_DATA_INI 0x90 -#define TVP5150_CC_DATA_END 0x93 - - /* WSS data registers */ -#define TVP5150_WSS_DATA_INI 0x94 -#define TVP5150_WSS_DATA_END 0x99 - -/* VPS data registers */ -#define TVP5150_VPS_DATA_INI 0x9a -#define TVP5150_VPS_DATA_END 0xa6 - -/* VITC data registers */ -#define TVP5150_VITC_DATA_INI 0xa7 -#define TVP5150_VITC_DATA_END 0xaf - -#define TVP5150_VBI_FIFO_READ_DATA 0xb0 /* VBI FIFO read data */ - -/* Teletext filter 1 */ -#define TVP5150_TELETEXT_FIL1_INI 0xb1 -#define TVP5150_TELETEXT_FIL1_END 0xb5 - -/* Teletext filter 2 */ -#define TVP5150_TELETEXT_FIL2_INI 0xb6 -#define TVP5150_TELETEXT_FIL2_END 0xba - -#define TVP5150_TELETEXT_FIL_ENA 0xbb /* Teletext filter enable */ -/* Reserved BCh-BFh */ -#define TVP5150_INT_STATUS_REG_A 0xc0 /* Interrupt status register A */ -#define TVP5150_INT_ENABLE_REG_A 0xc1 /* Interrupt enable register A */ -#define TVP5150_INT_CONF 0xc2 /* Interrupt configuration */ -#define TVP5150_VDP_CONF_RAM_DATA 0xc3 /* VDP configuration RAM data */ -#define TVP5150_CONF_RAM_ADDR_LOW 0xc4 /* Configuration RAM address low byte */ -#define TVP5150_CONF_RAM_ADDR_HIGH 0xc5 /* Configuration RAM address high byte */ -#define TVP5150_VDP_STATUS_REG 0xc6 /* VDP status register */ -#define TVP5150_FIFO_WORD_COUNT 0xc7 /* FIFO word count */ -#define TVP5150_FIFO_INT_THRESHOLD 0xc8 /* FIFO interrupt threshold */ -#define TVP5150_FIFO_RESET 0xc9 /* FIFO reset */ -#define TVP5150_LINE_NUMBER_INT 0xca /* Line number interrupt */ -#define TVP5150_PIX_ALIGN_REG_LOW 0xcb /* Pixel alignment register low byte */ -#define TVP5150_PIX_ALIGN_REG_HIGH 0xcc /* Pixel alignment register high byte */ -#define TVP5150_FIFO_OUT_CTRL 0xcd /* FIFO output control */ -/* Reserved CEh */ -#define TVP5150_FULL_FIELD_ENA 0xcf /* Full field enable 1 */ - -/* Line mode registers */ -#define TVP5150_LINE_MODE_INI 0xd0 -#define TVP5150_LINE_MODE_END 0xfb - -#define TVP5150_FULL_FIELD_MODE_REG 0xfc /* Full field mode register */ -/* Reserved FDh-FFh */ diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c deleted file mode 100644 index fb6a5b57eb8..00000000000 --- a/drivers/media/video/tvp7002.c +++ /dev/null @@ -1,1145 +0,0 @@ -/* Texas Instruments Triple 8-/10-BIT 165-/110-MSPS Video and Graphics - * Digitizer with Horizontal PLL registers - * - * Copyright (C) 2009 Texas Instruments Inc - * Author: Santiago Nunez-Corrales - * - * This code is partially based upon the TVP5150 driver - * written by Mauro Carvalho Chehab (mchehab@infradead.org), - * the TVP514x driver written by Vaibhav Hiremath - * and the TVP7002 driver in the TI LSP 2.10.00.14. Revisions by - * Muralidharan Karicheri and Snehaprabha Narnakaje (TI). - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "tvp7002_reg.h" - -MODULE_DESCRIPTION("TI TVP7002 Video and Graphics Digitizer driver"); -MODULE_AUTHOR("Santiago Nunez-Corrales "); -MODULE_LICENSE("GPL"); - -/* Module Name */ -#define TVP7002_MODULE_NAME "tvp7002" - -/* I2C retry attempts */ -#define I2C_RETRY_COUNT (5) - -/* End of registers */ -#define TVP7002_EOR 0x5c - -/* Read write definition for registers */ -#define TVP7002_READ 0 -#define TVP7002_WRITE 1 -#define TVP7002_RESERVED 2 - -/* Interlaced vs progressive mask and shift */ -#define TVP7002_IP_SHIFT 5 -#define TVP7002_INPR_MASK (0x01 << TVP7002_IP_SHIFT) - -/* Shift for CPL and LPF registers */ -#define TVP7002_CL_SHIFT 8 -#define TVP7002_CL_MASK 0x0f - -/* Debug functions */ -static bool debug; -module_param(debug, bool, 0644); -MODULE_PARM_DESC(debug, "Debug level (0-2)"); - -/* Structure for register values */ -struct i2c_reg_value { - u8 reg; - u8 value; - u8 type; -}; - -/* - * Register default values (according to tvp7002 datasheet) - * In the case of read-only registers, the value (0xff) is - * never written. R/W functionality is controlled by the - * writable bit in the register struct definition. - */ -static const struct i2c_reg_value tvp7002_init_default[] = { - { TVP7002_CHIP_REV, 0xff, TVP7002_READ }, - { TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE }, - { TVP7002_HPLL_FDBK_DIV_LSBS, 0x20, TVP7002_WRITE }, - { TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE }, - { TVP7002_HPLL_PHASE_SEL, 0x80, TVP7002_WRITE }, - { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, - { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, - { TVP7002_HSYNC_OUT_W, 0x60, TVP7002_WRITE }, - { TVP7002_B_FINE_GAIN, 0x00, TVP7002_WRITE }, - { TVP7002_G_FINE_GAIN, 0x00, TVP7002_WRITE }, - { TVP7002_R_FINE_GAIN, 0x00, TVP7002_WRITE }, - { TVP7002_B_FINE_OFF_MSBS, 0x80, TVP7002_WRITE }, - { TVP7002_G_FINE_OFF_MSBS, 0x80, TVP7002_WRITE }, - { TVP7002_R_FINE_OFF_MSBS, 0x80, TVP7002_WRITE }, - { TVP7002_SYNC_CTL_1, 0x20, TVP7002_WRITE }, - { TVP7002_HPLL_AND_CLAMP_CTL, 0x2e, TVP7002_WRITE }, - { TVP7002_SYNC_ON_G_THRS, 0x5d, TVP7002_WRITE }, - { TVP7002_SYNC_SEPARATOR_THRS, 0x47, TVP7002_WRITE }, - { TVP7002_HPLL_PRE_COAST, 0x00, TVP7002_WRITE }, - { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, - { TVP7002_SYNC_DETECT_STAT, 0xff, TVP7002_READ }, - { TVP7002_OUT_FORMATTER, 0x47, TVP7002_WRITE }, - { TVP7002_MISC_CTL_1, 0x01, TVP7002_WRITE }, - { TVP7002_MISC_CTL_2, 0x00, TVP7002_WRITE }, - { TVP7002_MISC_CTL_3, 0x01, TVP7002_WRITE }, - { TVP7002_IN_MUX_SEL_1, 0x00, TVP7002_WRITE }, - { TVP7002_IN_MUX_SEL_2, 0x67, TVP7002_WRITE }, - { TVP7002_B_AND_G_COARSE_GAIN, 0x77, TVP7002_WRITE }, - { TVP7002_R_COARSE_GAIN, 0x07, TVP7002_WRITE }, - { TVP7002_FINE_OFF_LSBS, 0x00, TVP7002_WRITE }, - { TVP7002_B_COARSE_OFF, 0x10, TVP7002_WRITE }, - { TVP7002_G_COARSE_OFF, 0x10, TVP7002_WRITE }, - { TVP7002_R_COARSE_OFF, 0x10, TVP7002_WRITE }, - { TVP7002_HSOUT_OUT_START, 0x08, TVP7002_WRITE }, - { TVP7002_MISC_CTL_4, 0x00, TVP7002_WRITE }, - { TVP7002_B_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ }, - { TVP7002_G_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ }, - { TVP7002_R_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ }, - { TVP7002_AUTO_LVL_CTL_ENABLE, 0x80, TVP7002_WRITE }, - { TVP7002_DGTL_ALC_OUT_MSBS, 0xff, TVP7002_READ }, - { TVP7002_AUTO_LVL_CTL_FILTER, 0x53, TVP7002_WRITE }, - { 0x29, 0x08, TVP7002_RESERVED }, - { TVP7002_FINE_CLAMP_CTL, 0x07, TVP7002_WRITE }, - /* PWR_CTL is controlled only by the probe and reset functions */ - { TVP7002_PWR_CTL, 0x00, TVP7002_RESERVED }, - { TVP7002_ADC_SETUP, 0x50, TVP7002_WRITE }, - { TVP7002_COARSE_CLAMP_CTL, 0x00, TVP7002_WRITE }, - { TVP7002_SOG_CLAMP, 0x80, TVP7002_WRITE }, - { TVP7002_RGB_COARSE_CLAMP_CTL, 0x8c, TVP7002_WRITE }, - { TVP7002_SOG_COARSE_CLAMP_CTL, 0x04, TVP7002_WRITE }, - { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, - { 0x32, 0x18, TVP7002_RESERVED }, - { 0x33, 0x60, TVP7002_RESERVED }, - { TVP7002_MVIS_STRIPPER_W, 0xff, TVP7002_RESERVED }, - { TVP7002_VSYNC_ALGN, 0x10, TVP7002_WRITE }, - { TVP7002_SYNC_BYPASS, 0x00, TVP7002_WRITE }, - { TVP7002_L_FRAME_STAT_LSBS, 0xff, TVP7002_READ }, - { TVP7002_L_FRAME_STAT_MSBS, 0xff, TVP7002_READ }, - { TVP7002_CLK_L_STAT_LSBS, 0xff, TVP7002_READ }, - { TVP7002_CLK_L_STAT_MSBS, 0xff, TVP7002_READ }, - { TVP7002_HSYNC_W, 0xff, TVP7002_READ }, - { TVP7002_VSYNC_W, 0xff, TVP7002_READ }, - { TVP7002_L_LENGTH_TOL, 0x03, TVP7002_WRITE }, - { 0x3e, 0x60, TVP7002_RESERVED }, - { TVP7002_VIDEO_BWTH_CTL, 0x01, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_LSBS, 0x01, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_MSBS, 0x2c, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_LSBS, 0x06, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_MSBS, 0x2c, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_DURATION, 0x1e, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, - { TVP7002_FBIT_F_0_START_L_OFF, 0x00, TVP7002_WRITE }, - { TVP7002_FBIT_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, - { TVP7002_YUV_Y_G_COEF_LSBS, 0xe3, TVP7002_WRITE }, - { TVP7002_YUV_Y_G_COEF_MSBS, 0x16, TVP7002_WRITE }, - { TVP7002_YUV_Y_B_COEF_LSBS, 0x4f, TVP7002_WRITE }, - { TVP7002_YUV_Y_B_COEF_MSBS, 0x02, TVP7002_WRITE }, - { TVP7002_YUV_Y_R_COEF_LSBS, 0xce, TVP7002_WRITE }, - { TVP7002_YUV_Y_R_COEF_MSBS, 0x06, TVP7002_WRITE }, - { TVP7002_YUV_U_G_COEF_LSBS, 0xab, TVP7002_WRITE }, - { TVP7002_YUV_U_G_COEF_MSBS, 0xf3, TVP7002_WRITE }, - { TVP7002_YUV_U_B_COEF_LSBS, 0x00, TVP7002_WRITE }, - { TVP7002_YUV_U_B_COEF_MSBS, 0x10, TVP7002_WRITE }, - { TVP7002_YUV_U_R_COEF_LSBS, 0x55, TVP7002_WRITE }, - { TVP7002_YUV_U_R_COEF_MSBS, 0xfc, TVP7002_WRITE }, - { TVP7002_YUV_V_G_COEF_LSBS, 0x78, TVP7002_WRITE }, - { TVP7002_YUV_V_G_COEF_MSBS, 0xf1, TVP7002_WRITE }, - { TVP7002_YUV_V_B_COEF_LSBS, 0x88, TVP7002_WRITE }, - { TVP7002_YUV_V_B_COEF_MSBS, 0xfe, TVP7002_WRITE }, - { TVP7002_YUV_V_R_COEF_LSBS, 0x00, TVP7002_WRITE }, - { TVP7002_YUV_V_R_COEF_MSBS, 0x10, TVP7002_WRITE }, - /* This signals end of register values */ - { TVP7002_EOR, 0xff, TVP7002_RESERVED } -}; - -/* Register parameters for 480P */ -static const struct i2c_reg_value tvp7002_parms_480P[] = { - { TVP7002_HPLL_FDBK_DIV_MSBS, 0x35, TVP7002_WRITE }, - { TVP7002_HPLL_FDBK_DIV_LSBS, 0xa0, TVP7002_WRITE }, - { TVP7002_HPLL_CRTL, 0x02, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_LSBS, 0x91, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_MSBS, 0x00, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_LSBS, 0x0B, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_MSBS, 0x00, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_START_L_OFF, 0x03, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_START_L_OFF, 0x01, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_DURATION, 0x13, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_DURATION, 0x13, TVP7002_WRITE }, - { TVP7002_ALC_PLACEMENT, 0x18, TVP7002_WRITE }, - { TVP7002_CLAMP_START, 0x06, TVP7002_WRITE }, - { TVP7002_CLAMP_W, 0x10, TVP7002_WRITE }, - { TVP7002_HPLL_PRE_COAST, 0x03, TVP7002_WRITE }, - { TVP7002_HPLL_POST_COAST, 0x03, TVP7002_WRITE }, - { TVP7002_EOR, 0xff, TVP7002_RESERVED } -}; - -/* Register parameters for 576P */ -static const struct i2c_reg_value tvp7002_parms_576P[] = { - { TVP7002_HPLL_FDBK_DIV_MSBS, 0x36, TVP7002_WRITE }, - { TVP7002_HPLL_FDBK_DIV_LSBS, 0x00, TVP7002_WRITE }, - { TVP7002_HPLL_CRTL, 0x18, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_LSBS, 0x9B, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_MSBS, 0x00, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_LSBS, 0x0F, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_MSBS, 0x00, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_START_L_OFF, 0x00, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, - { TVP7002_ALC_PLACEMENT, 0x18, TVP7002_WRITE }, - { TVP7002_CLAMP_START, 0x06, TVP7002_WRITE }, - { TVP7002_CLAMP_W, 0x10, TVP7002_WRITE }, - { TVP7002_HPLL_PRE_COAST, 0x03, TVP7002_WRITE }, - { TVP7002_HPLL_POST_COAST, 0x03, TVP7002_WRITE }, - { TVP7002_EOR, 0xff, TVP7002_RESERVED } -}; - -/* Register parameters for 1080I60 */ -static const struct i2c_reg_value tvp7002_parms_1080I60[] = { - { TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE }, - { TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE }, - { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE }, - { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, - { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, - { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, - { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, - { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, - { TVP7002_EOR, 0xff, TVP7002_RESERVED } -}; - -/* Register parameters for 1080P60 */ -static const struct i2c_reg_value tvp7002_parms_1080P60[] = { - { TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE }, - { TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE }, - { TVP7002_HPLL_CRTL, 0xE0, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE }, - { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, - { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, - { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, - { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, - { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, - { TVP7002_EOR, 0xff, TVP7002_RESERVED } -}; - -/* Register parameters for 1080I50 */ -static const struct i2c_reg_value tvp7002_parms_1080I50[] = { - { TVP7002_HPLL_FDBK_DIV_MSBS, 0xa5, TVP7002_WRITE }, - { TVP7002_HPLL_FDBK_DIV_LSBS, 0x00, TVP7002_WRITE }, - { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE }, - { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, - { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, - { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, - { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, - { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, - { TVP7002_EOR, 0xff, TVP7002_RESERVED } -}; - -/* Register parameters for 720P60 */ -static const struct i2c_reg_value tvp7002_parms_720P60[] = { - { TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE }, - { TVP7002_HPLL_FDBK_DIV_LSBS, 0x20, TVP7002_WRITE }, - { TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_LSBS, 0x4B, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_MSBS, 0x06, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, - { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, - { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, - { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, - { TVP7002_HPLL_PRE_COAST, 0x00, TVP7002_WRITE }, - { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, - { TVP7002_EOR, 0xff, TVP7002_RESERVED } -}; - -/* Register parameters for 720P50 */ -static const struct i2c_reg_value tvp7002_parms_720P50[] = { - { TVP7002_HPLL_FDBK_DIV_MSBS, 0x7b, TVP7002_WRITE }, - { TVP7002_HPLL_FDBK_DIV_LSBS, 0xc0, TVP7002_WRITE }, - { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_LSBS, 0x4B, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_MSBS, 0x06, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, - { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, - { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, - { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, - { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, - { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, - { TVP7002_EOR, 0xff, TVP7002_RESERVED } -}; - -/* Preset definition for handling device operation */ -struct tvp7002_preset_definition { - u32 preset; - struct v4l2_dv_timings timings; - const struct i2c_reg_value *p_settings; - enum v4l2_colorspace color_space; - enum v4l2_field scanmode; - u16 progressive; - u16 lines_per_frame; - u16 cpl_min; - u16 cpl_max; -}; - -/* Struct list for digital video presets */ -static const struct tvp7002_preset_definition tvp7002_presets[] = { - { - V4L2_DV_720P60, - V4L2_DV_BT_CEA_1280X720P60, - tvp7002_parms_720P60, - V4L2_COLORSPACE_REC709, - V4L2_FIELD_NONE, - 1, - 0x2EE, - 135, - 153 - }, - { - V4L2_DV_1080I60, - V4L2_DV_BT_CEA_1920X1080I60, - tvp7002_parms_1080I60, - V4L2_COLORSPACE_REC709, - V4L2_FIELD_INTERLACED, - 0, - 0x465, - 181, - 205 - }, - { - V4L2_DV_1080I50, - V4L2_DV_BT_CEA_1920X1080I50, - tvp7002_parms_1080I50, - V4L2_COLORSPACE_REC709, - V4L2_FIELD_INTERLACED, - 0, - 0x465, - 217, - 245 - }, - { - V4L2_DV_720P50, - V4L2_DV_BT_CEA_1280X720P50, - tvp7002_parms_720P50, - V4L2_COLORSPACE_REC709, - V4L2_FIELD_NONE, - 1, - 0x2EE, - 163, - 183 - }, - { - V4L2_DV_1080P60, - V4L2_DV_BT_CEA_1920X1080P60, - tvp7002_parms_1080P60, - V4L2_COLORSPACE_REC709, - V4L2_FIELD_NONE, - 1, - 0x465, - 90, - 102 - }, - { - V4L2_DV_480P59_94, - V4L2_DV_BT_CEA_720X480P59_94, - tvp7002_parms_480P, - V4L2_COLORSPACE_SMPTE170M, - V4L2_FIELD_NONE, - 1, - 0x20D, - 0xffff, - 0xffff - }, - { - V4L2_DV_576P50, - V4L2_DV_BT_CEA_720X576P50, - tvp7002_parms_576P, - V4L2_COLORSPACE_SMPTE170M, - V4L2_FIELD_NONE, - 1, - 0x271, - 0xffff, - 0xffff - } -}; - -#define NUM_PRESETS ARRAY_SIZE(tvp7002_presets) - -/* Device definition */ -struct tvp7002 { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - const struct tvp7002_config *pdata; - - int ver; - int streaming; - - const struct tvp7002_preset_definition *current_preset; -}; - -/* - * to_tvp7002 - Obtain device handler TVP7002 - * @sd: ptr to v4l2_subdev struct - * - * Returns device handler tvp7002. - */ -static inline struct tvp7002 *to_tvp7002(struct v4l2_subdev *sd) -{ - return container_of(sd, struct tvp7002, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct tvp7002, hdl)->sd; -} - -/* - * tvp7002_read - Read a value from a register in an TVP7002 - * @sd: ptr to v4l2_subdev struct - * @addr: TVP7002 register address - * @dst: pointer to 8-bit destination - * - * Returns value read if successful, or non-zero (-1) otherwise. - */ -static int tvp7002_read(struct v4l2_subdev *sd, u8 addr, u8 *dst) -{ - struct i2c_client *c = v4l2_get_subdevdata(sd); - int retry; - int error; - - for (retry = 0; retry < I2C_RETRY_COUNT; retry++) { - error = i2c_smbus_read_byte_data(c, addr); - - if (error >= 0) { - *dst = (u8)error; - return 0; - } - - msleep_interruptible(10); - } - v4l2_err(sd, "TVP7002 read error %d\n", error); - return error; -} - -/* - * tvp7002_read_err() - Read a register value with error code - * @sd: pointer to standard V4L2 sub-device structure - * @reg: destination register - * @val: value to be read - * @err: pointer to error value - * - * Read a value in a register and save error value in pointer. - * Also update the register table if successful - */ -static inline void tvp7002_read_err(struct v4l2_subdev *sd, u8 reg, - u8 *dst, int *err) -{ - if (!*err) - *err = tvp7002_read(sd, reg, dst); -} - -/* - * tvp7002_write() - Write a value to a register in TVP7002 - * @sd: ptr to v4l2_subdev struct - * @addr: TVP7002 register address - * @value: value to be written to the register - * - * Write a value to a register in an TVP7002 decoder device. - * Returns zero if successful, or non-zero otherwise. - */ -static int tvp7002_write(struct v4l2_subdev *sd, u8 addr, u8 value) -{ - struct i2c_client *c; - int retry; - int error; - - c = v4l2_get_subdevdata(sd); - - for (retry = 0; retry < I2C_RETRY_COUNT; retry++) { - error = i2c_smbus_write_byte_data(c, addr, value); - - if (error >= 0) - return 0; - - v4l2_warn(sd, "Write: retry ... %d\n", retry); - msleep_interruptible(10); - } - v4l2_err(sd, "TVP7002 write error %d\n", error); - return error; -} - -/* - * tvp7002_write_err() - Write a register value with error code - * @sd: pointer to standard V4L2 sub-device structure - * @reg: destination register - * @val: value to be written - * @err: pointer to error value - * - * Write a value in a register and save error value in pointer. - * Also update the register table if successful - */ -static inline void tvp7002_write_err(struct v4l2_subdev *sd, u8 reg, - u8 val, int *err) -{ - if (!*err) - *err = tvp7002_write(sd, reg, val); -} - -/* - * tvp7002_g_chip_ident() - Get chip identification number - * @sd: ptr to v4l2_subdev struct - * @chip: ptr to v4l2_dbg_chip_ident struct - * - * Obtains the chip's identification number. - * Returns zero or -EINVAL if read operation fails. - */ -static int tvp7002_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *chip) -{ - u8 rev; - int error; - struct i2c_client *client = v4l2_get_subdevdata(sd); - - error = tvp7002_read(sd, TVP7002_CHIP_REV, &rev); - - if (error < 0) - return error; - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP7002, rev); -} - -/* - * tvp7002_write_inittab() - Write initialization values - * @sd: ptr to v4l2_subdev struct - * @regs: ptr to i2c_reg_value struct - * - * Write initialization values. - * Returns zero or -EINVAL if read operation fails. - */ -static int tvp7002_write_inittab(struct v4l2_subdev *sd, - const struct i2c_reg_value *regs) -{ - int error = 0; - - /* Initialize the first (defined) registers */ - while (TVP7002_EOR != regs->reg) { - if (TVP7002_WRITE == regs->type) - tvp7002_write_err(sd, regs->reg, regs->value, &error); - regs++; - } - - return error; -} - -/* - * tvp7002_s_dv_preset() - Set digital video preset - * @sd: ptr to v4l2_subdev struct - * @dv_preset: ptr to v4l2_dv_preset struct - * - * Set the digital video preset for a TVP7002 decoder device. - * Returns zero when successful or -EINVAL if register access fails. - */ -static int tvp7002_s_dv_preset(struct v4l2_subdev *sd, - struct v4l2_dv_preset *dv_preset) -{ - struct tvp7002 *device = to_tvp7002(sd); - u32 preset; - int i; - - for (i = 0; i < NUM_PRESETS; i++) { - preset = tvp7002_presets[i].preset; - if (preset == dv_preset->preset) { - device->current_preset = &tvp7002_presets[i]; - return tvp7002_write_inittab(sd, tvp7002_presets[i].p_settings); - } - } - - return -EINVAL; -} - -static int tvp7002_s_dv_timings(struct v4l2_subdev *sd, - struct v4l2_dv_timings *dv_timings) -{ - struct tvp7002 *device = to_tvp7002(sd); - const struct v4l2_bt_timings *bt = &dv_timings->bt; - int i; - - if (dv_timings->type != V4L2_DV_BT_656_1120) - return -EINVAL; - for (i = 0; i < NUM_PRESETS; i++) { - const struct v4l2_bt_timings *t = &tvp7002_presets[i].timings.bt; - - if (!memcmp(bt, t, &bt->standards - &bt->width)) { - device->current_preset = &tvp7002_presets[i]; - return tvp7002_write_inittab(sd, tvp7002_presets[i].p_settings); - } - } - return -EINVAL; -} - -static int tvp7002_g_dv_timings(struct v4l2_subdev *sd, - struct v4l2_dv_timings *dv_timings) -{ - struct tvp7002 *device = to_tvp7002(sd); - - *dv_timings = device->current_preset->timings; - return 0; -} - -/* - * tvp7002_s_ctrl() - Set a control - * @ctrl: ptr to v4l2_ctrl struct - * - * Set a control in TVP7002 decoder device. - * Returns zero when successful or -EINVAL if register access fails. - */ -static int tvp7002_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - int error = 0; - - switch (ctrl->id) { - case V4L2_CID_GAIN: - tvp7002_write_err(sd, TVP7002_R_FINE_GAIN, ctrl->val, &error); - tvp7002_write_err(sd, TVP7002_G_FINE_GAIN, ctrl->val, &error); - tvp7002_write_err(sd, TVP7002_B_FINE_GAIN, ctrl->val, &error); - return error; - } - return -EINVAL; -} - -/* - * tvp7002_mbus_fmt() - V4L2 decoder interface handler for try/s/g_mbus_fmt - * @sd: pointer to standard V4L2 sub-device structure - * @f: pointer to mediabus format structure - * - * Negotiate the image capture size and mediabus format. - * There is only one possible format, so this single function works for - * get, set and try. - */ -static int tvp7002_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) -{ - struct tvp7002 *device = to_tvp7002(sd); - struct v4l2_dv_enum_preset e_preset; - int error; - - /* Calculate height and width based on current standard */ - error = v4l_fill_dv_preset_info(device->current_preset->preset, &e_preset); - if (error) - return error; - - f->width = e_preset.width; - f->height = e_preset.height; - f->code = V4L2_MBUS_FMT_YUYV10_1X20; - f->field = device->current_preset->scanmode; - f->colorspace = device->current_preset->color_space; - - v4l2_dbg(1, debug, sd, "MBUS_FMT: Width - %d, Height - %d", - f->width, f->height); - return 0; -} - -/* - * tvp7002_query_dv_preset() - query DV preset - * @sd: pointer to standard V4L2 sub-device structure - * @qpreset: standard V4L2 v4l2_dv_preset structure - * - * Returns the current DV preset by TVP7002. If no active input is - * detected, returns -EINVAL - */ -static int tvp7002_query_dv(struct v4l2_subdev *sd, int *index) -{ - const struct tvp7002_preset_definition *presets = tvp7002_presets; - u8 progressive; - u32 lpfr; - u32 cpln; - int error = 0; - u8 lpf_lsb; - u8 lpf_msb; - u8 cpl_lsb; - u8 cpl_msb; - - /* Return invalid index if no active input is detected */ - *index = NUM_PRESETS; - - /* Read standards from device registers */ - tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_LSBS, &lpf_lsb, &error); - tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_MSBS, &lpf_msb, &error); - - if (error < 0) - return error; - - tvp7002_read_err(sd, TVP7002_CLK_L_STAT_LSBS, &cpl_lsb, &error); - tvp7002_read_err(sd, TVP7002_CLK_L_STAT_MSBS, &cpl_msb, &error); - - if (error < 0) - return error; - - /* Get lines per frame, clocks per line and interlaced/progresive */ - lpfr = lpf_lsb | ((TVP7002_CL_MASK & lpf_msb) << TVP7002_CL_SHIFT); - cpln = cpl_lsb | ((TVP7002_CL_MASK & cpl_msb) << TVP7002_CL_SHIFT); - progressive = (lpf_msb & TVP7002_INPR_MASK) >> TVP7002_IP_SHIFT; - - /* Do checking of video modes */ - for (*index = 0; *index < NUM_PRESETS; (*index)++, presets++) - if (lpfr == presets->lines_per_frame && - progressive == presets->progressive) { - if (presets->cpl_min == 0xffff) - break; - if (cpln >= presets->cpl_min && cpln <= presets->cpl_max) - break; - } - - if (*index == NUM_PRESETS) { - v4l2_dbg(1, debug, sd, "detection failed: lpf = %x, cpl = %x\n", - lpfr, cpln); - return -ENOLINK; - } - - /* Update lines per frame and clocks per line info */ - v4l2_dbg(1, debug, sd, "detected preset: %d\n", *index); - return 0; -} - -static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, - struct v4l2_dv_preset *qpreset) -{ - int index; - int err = tvp7002_query_dv(sd, &index); - - if (err || index == NUM_PRESETS) { - qpreset->preset = V4L2_DV_INVALID; - if (err == -ENOLINK) - err = 0; - return err; - } - qpreset->preset = tvp7002_presets[index].preset; - return 0; -} - -static int tvp7002_query_dv_timings(struct v4l2_subdev *sd, - struct v4l2_dv_timings *timings) -{ - int index; - int err = tvp7002_query_dv(sd, &index); - - if (err) - return err; - *timings = tvp7002_presets[index].timings; - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -/* - * tvp7002_g_register() - Get the value of a register - * @sd: ptr to v4l2_subdev struct - * @reg: ptr to v4l2_dbg_register struct - * - * Get the value of a TVP7002 decoder device register. - * Returns zero when successful, -EINVAL if register read fails or - * access to I2C client fails, -EPERM if the call is not allowed - * by disabled CAP_SYS_ADMIN. - */ -static int tvp7002_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 val; - int ret; - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - ret = tvp7002_read(sd, reg->reg & 0xff, &val); - reg->val = val; - return ret; -} - -/* - * tvp7002_s_register() - set a control - * @sd: ptr to v4l2_subdev struct - * @reg: ptr to v4l2_dbg_register struct - * - * Get the value of a TVP7002 decoder device register. - * Returns zero when successful, -EINVAL if register read fails or - * -EPERM if call not allowed. - */ -static int tvp7002_s_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - return tvp7002_write(sd, reg->reg & 0xff, reg->val & 0xff); -} -#endif - -/* - * tvp7002_enum_mbus_fmt() - Enum supported mediabus formats - * @sd: pointer to standard V4L2 sub-device structure - * @index: format index - * @code: pointer to mediabus format - * - * Enumerate supported mediabus formats. - */ - -static int tvp7002_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, - enum v4l2_mbus_pixelcode *code) -{ - /* Check requested format index is within range */ - if (index) - return -EINVAL; - *code = V4L2_MBUS_FMT_YUYV10_1X20; - return 0; -} - -/* - * tvp7002_s_stream() - V4L2 decoder i/f handler for s_stream - * @sd: pointer to standard V4L2 sub-device structure - * @enable: streaming enable or disable - * - * Sets streaming to enable or disable, if possible. - */ -static int tvp7002_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct tvp7002 *device = to_tvp7002(sd); - int error = 0; - - if (device->streaming == enable) - return 0; - - if (enable) { - /* Set output state on (low impedance means stream on) */ - error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x00); - device->streaming = enable; - } else { - /* Set output state off (high impedance means stream off) */ - error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x03); - if (error) - v4l2_dbg(1, debug, sd, "Unable to stop streaming\n"); - - device->streaming = enable; - } - - return error; -} - -/* - * tvp7002_log_status() - Print information about register settings - * @sd: ptr to v4l2_subdev struct - * - * Log register values of a TVP7002 decoder device. - * Returns zero or -EINVAL if read operation fails. - */ -static int tvp7002_log_status(struct v4l2_subdev *sd) -{ - const struct tvp7002_preset_definition *presets = tvp7002_presets; - struct tvp7002 *device = to_tvp7002(sd); - struct v4l2_dv_enum_preset e_preset; - struct v4l2_dv_preset detected; - int i; - - detected.preset = V4L2_DV_INVALID; - /* Find my current standard*/ - tvp7002_query_dv_preset(sd, &detected); - - /* Print standard related code values */ - for (i = 0; i < NUM_PRESETS; i++, presets++) - if (presets->preset == detected.preset) - break; - - if (v4l_fill_dv_preset_info(device->current_preset->preset, &e_preset)) - return -EINVAL; - - v4l2_info(sd, "Selected DV Preset: %s\n", e_preset.name); - v4l2_info(sd, " Pixels per line: %u\n", e_preset.width); - v4l2_info(sd, " Lines per frame: %u\n\n", e_preset.height); - if (i == NUM_PRESETS) { - v4l2_info(sd, "Detected DV Preset: None\n"); - } else { - if (v4l_fill_dv_preset_info(presets->preset, &e_preset)) - return -EINVAL; - v4l2_info(sd, "Detected DV Preset: %s\n", e_preset.name); - v4l2_info(sd, " Pixels per line: %u\n", e_preset.width); - v4l2_info(sd, " Lines per frame: %u\n\n", e_preset.height); - } - v4l2_info(sd, "Streaming enabled: %s\n", - device->streaming ? "yes" : "no"); - - /* Print the current value of the gain control */ - v4l2_ctrl_handler_log_status(&device->hdl, sd->name); - - return 0; -} - -/* - * tvp7002_enum_dv_presets() - Enum supported digital video formats - * @sd: pointer to standard V4L2 sub-device structure - * @preset: pointer to format struct - * - * Enumerate supported digital video formats. - */ -static int tvp7002_enum_dv_presets(struct v4l2_subdev *sd, - struct v4l2_dv_enum_preset *preset) -{ - /* Check requested format index is within range */ - if (preset->index >= NUM_PRESETS) - return -EINVAL; - - return v4l_fill_dv_preset_info(tvp7002_presets[preset->index].preset, preset); -} - -static int tvp7002_enum_dv_timings(struct v4l2_subdev *sd, - struct v4l2_enum_dv_timings *timings) -{ - /* Check requested format index is within range */ - if (timings->index >= NUM_PRESETS) - return -EINVAL; - - timings->timings = tvp7002_presets[timings->index].timings; - return 0; -} - -static const struct v4l2_ctrl_ops tvp7002_ctrl_ops = { - .s_ctrl = tvp7002_s_ctrl, -}; - -/* V4L2 core operation handlers */ -static const struct v4l2_subdev_core_ops tvp7002_core_ops = { - .g_chip_ident = tvp7002_g_chip_ident, - .log_status = tvp7002_log_status, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = tvp7002_g_register, - .s_register = tvp7002_s_register, -#endif -}; - -/* Specific video subsystem operation handlers */ -static const struct v4l2_subdev_video_ops tvp7002_video_ops = { - .enum_dv_presets = tvp7002_enum_dv_presets, - .s_dv_preset = tvp7002_s_dv_preset, - .query_dv_preset = tvp7002_query_dv_preset, - .g_dv_timings = tvp7002_g_dv_timings, - .s_dv_timings = tvp7002_s_dv_timings, - .enum_dv_timings = tvp7002_enum_dv_timings, - .query_dv_timings = tvp7002_query_dv_timings, - .s_stream = tvp7002_s_stream, - .g_mbus_fmt = tvp7002_mbus_fmt, - .try_mbus_fmt = tvp7002_mbus_fmt, - .s_mbus_fmt = tvp7002_mbus_fmt, - .enum_mbus_fmt = tvp7002_enum_mbus_fmt, -}; - -/* V4L2 top level operation handlers */ -static const struct v4l2_subdev_ops tvp7002_ops = { - .core = &tvp7002_core_ops, - .video = &tvp7002_video_ops, -}; - -/* - * tvp7002_probe - Probe a TVP7002 device - * @c: ptr to i2c_client struct - * @id: ptr to i2c_device_id struct - * - * Initialize the TVP7002 device - * Returns zero when successful, -EINVAL if register read fails or - * -EIO if i2c access is not available. - */ -static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id) -{ - struct v4l2_subdev *sd; - struct tvp7002 *device; - struct v4l2_dv_preset preset; - int polarity_a; - int polarity_b; - u8 revision; - - int error; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(c->adapter, - I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) - return -EIO; - - if (!c->dev.platform_data) { - v4l_err(c, "No platform data!!\n"); - return -ENODEV; - } - - device = kzalloc(sizeof(struct tvp7002), GFP_KERNEL); - - if (!device) - return -ENOMEM; - - sd = &device->sd; - device->pdata = c->dev.platform_data; - device->current_preset = tvp7002_presets; - - /* Tell v4l2 the device is ready */ - v4l2_i2c_subdev_init(sd, c, &tvp7002_ops); - v4l_info(c, "tvp7002 found @ 0x%02x (%s)\n", - c->addr, c->adapter->name); - - error = tvp7002_read(sd, TVP7002_CHIP_REV, &revision); - if (error < 0) - goto found_error; - - /* Get revision number */ - v4l2_info(sd, "Rev. %02x detected.\n", revision); - if (revision != 0x02) - v4l2_info(sd, "Unknown revision detected.\n"); - - /* Initializes TVP7002 to its default values */ - error = tvp7002_write_inittab(sd, tvp7002_init_default); - - if (error < 0) - goto found_error; - - /* Set polarity information after registers have been set */ - polarity_a = 0x20 | device->pdata->hs_polarity << 5 - | device->pdata->vs_polarity << 2; - error = tvp7002_write(sd, TVP7002_SYNC_CTL_1, polarity_a); - if (error < 0) - goto found_error; - - polarity_b = 0x01 | device->pdata->fid_polarity << 2 - | device->pdata->sog_polarity << 1 - | device->pdata->clk_polarity; - error = tvp7002_write(sd, TVP7002_MISC_CTL_3, polarity_b); - if (error < 0) - goto found_error; - - /* Set registers according to default video mode */ - preset.preset = device->current_preset->preset; - error = tvp7002_s_dv_preset(sd, &preset); - - v4l2_ctrl_handler_init(&device->hdl, 1); - v4l2_ctrl_new_std(&device->hdl, &tvp7002_ctrl_ops, - V4L2_CID_GAIN, 0, 255, 1, 0); - sd->ctrl_handler = &device->hdl; - if (device->hdl.error) { - int err = device->hdl.error; - - v4l2_ctrl_handler_free(&device->hdl); - kfree(device); - return err; - } - v4l2_ctrl_handler_setup(&device->hdl); - -found_error: - if (error < 0) - kfree(device); - - return error; -} - -/* - * tvp7002_remove - Remove TVP7002 device support - * @c: ptr to i2c_client struct - * - * Reset the TVP7002 device - * Returns zero. - */ -static int tvp7002_remove(struct i2c_client *c) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(c); - struct tvp7002 *device = to_tvp7002(sd); - - v4l2_dbg(1, debug, sd, "Removing tvp7002 adapter" - "on address 0x%x\n", c->addr); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&device->hdl); - kfree(device); - return 0; -} - -/* I2C Device ID table */ -static const struct i2c_device_id tvp7002_id[] = { - { "tvp7002", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tvp7002_id); - -/* I2C driver data */ -static struct i2c_driver tvp7002_driver = { - .driver = { - .owner = THIS_MODULE, - .name = TVP7002_MODULE_NAME, - }, - .probe = tvp7002_probe, - .remove = tvp7002_remove, - .id_table = tvp7002_id, -}; - -module_i2c_driver(tvp7002_driver); diff --git a/drivers/media/video/tvp7002_reg.h b/drivers/media/video/tvp7002_reg.h deleted file mode 100644 index 0e34ca9bccf..00000000000 --- a/drivers/media/video/tvp7002_reg.h +++ /dev/null @@ -1,150 +0,0 @@ -/* Texas Instruments Triple 8-/10-BIT 165-/110-MSPS Video and Graphics - * Digitizer with Horizontal PLL registers - * - * Copyright (C) 2009 Texas Instruments Inc - * Author: Santiago Nunez-Corrales - * - * This code is partially based upon the TVP5150 driver - * written by Mauro Carvalho Chehab (mchehab@infradead.org), - * the TVP514x driver written by Vaibhav Hiremath - * and the TVP7002 driver in the TI LSP 2.10.00.14 - * - * 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. - */ - -/* Naming conventions - * ------------------ - * - * FDBK: Feedback - * DIV: Divider - * CTL: Control - * SEL: Select - * IN: Input - * OUT: Output - * R: Red - * G: Green - * B: Blue - * OFF: Offset - * THRS: Threshold - * DGTL: Digital - * LVL: Level - * PWR: Power - * MVIS: Macrovision - * W: Width - * H: Height - * ALGN: Alignment - * CLK: Clocks - * TOL: Tolerance - * BWTH: Bandwidth - * COEF: Coefficient - * STAT: Status - * AUTO: Automatic - * FLD: Field - * L: Line - */ - -#define TVP7002_CHIP_REV 0x00 -#define TVP7002_HPLL_FDBK_DIV_MSBS 0x01 -#define TVP7002_HPLL_FDBK_DIV_LSBS 0x02 -#define TVP7002_HPLL_CRTL 0x03 -#define TVP7002_HPLL_PHASE_SEL 0x04 -#define TVP7002_CLAMP_START 0x05 -#define TVP7002_CLAMP_W 0x06 -#define TVP7002_HSYNC_OUT_W 0x07 -#define TVP7002_B_FINE_GAIN 0x08 -#define TVP7002_G_FINE_GAIN 0x09 -#define TVP7002_R_FINE_GAIN 0x0a -#define TVP7002_B_FINE_OFF_MSBS 0x0b -#define TVP7002_G_FINE_OFF_MSBS 0x0c -#define TVP7002_R_FINE_OFF_MSBS 0x0d -#define TVP7002_SYNC_CTL_1 0x0e -#define TVP7002_HPLL_AND_CLAMP_CTL 0x0f -#define TVP7002_SYNC_ON_G_THRS 0x10 -#define TVP7002_SYNC_SEPARATOR_THRS 0x11 -#define TVP7002_HPLL_PRE_COAST 0x12 -#define TVP7002_HPLL_POST_COAST 0x13 -#define TVP7002_SYNC_DETECT_STAT 0x14 -#define TVP7002_OUT_FORMATTER 0x15 -#define TVP7002_MISC_CTL_1 0x16 -#define TVP7002_MISC_CTL_2 0x17 -#define TVP7002_MISC_CTL_3 0x18 -#define TVP7002_IN_MUX_SEL_1 0x19 -#define TVP7002_IN_MUX_SEL_2 0x1a -#define TVP7002_B_AND_G_COARSE_GAIN 0x1b -#define TVP7002_R_COARSE_GAIN 0x1c -#define TVP7002_FINE_OFF_LSBS 0x1d -#define TVP7002_B_COARSE_OFF 0x1e -#define TVP7002_G_COARSE_OFF 0x1f -#define TVP7002_R_COARSE_OFF 0x20 -#define TVP7002_HSOUT_OUT_START 0x21 -#define TVP7002_MISC_CTL_4 0x22 -#define TVP7002_B_DGTL_ALC_OUT_LSBS 0x23 -#define TVP7002_G_DGTL_ALC_OUT_LSBS 0x24 -#define TVP7002_R_DGTL_ALC_OUT_LSBS 0x25 -#define TVP7002_AUTO_LVL_CTL_ENABLE 0x26 -#define TVP7002_DGTL_ALC_OUT_MSBS 0x27 -#define TVP7002_AUTO_LVL_CTL_FILTER 0x28 -/* Reserved 0x29*/ -#define TVP7002_FINE_CLAMP_CTL 0x2a -#define TVP7002_PWR_CTL 0x2b -#define TVP7002_ADC_SETUP 0x2c -#define TVP7002_COARSE_CLAMP_CTL 0x2d -#define TVP7002_SOG_CLAMP 0x2e -#define TVP7002_RGB_COARSE_CLAMP_CTL 0x2f -#define TVP7002_SOG_COARSE_CLAMP_CTL 0x30 -#define TVP7002_ALC_PLACEMENT 0x31 -/* Reserved 0x32 */ -/* Reserved 0x33 */ -#define TVP7002_MVIS_STRIPPER_W 0x34 -#define TVP7002_VSYNC_ALGN 0x35 -#define TVP7002_SYNC_BYPASS 0x36 -#define TVP7002_L_FRAME_STAT_LSBS 0x37 -#define TVP7002_L_FRAME_STAT_MSBS 0x38 -#define TVP7002_CLK_L_STAT_LSBS 0x39 -#define TVP7002_CLK_L_STAT_MSBS 0x3a -#define TVP7002_HSYNC_W 0x3b -#define TVP7002_VSYNC_W 0x3c -#define TVP7002_L_LENGTH_TOL 0x3d -/* Reserved 0x3e */ -#define TVP7002_VIDEO_BWTH_CTL 0x3f -#define TVP7002_AVID_START_PIXEL_LSBS 0x40 -#define TVP7002_AVID_START_PIXEL_MSBS 0x41 -#define TVP7002_AVID_STOP_PIXEL_LSBS 0x42 -#define TVP7002_AVID_STOP_PIXEL_MSBS 0x43 -#define TVP7002_VBLK_F_0_START_L_OFF 0x44 -#define TVP7002_VBLK_F_1_START_L_OFF 0x45 -#define TVP7002_VBLK_F_0_DURATION 0x46 -#define TVP7002_VBLK_F_1_DURATION 0x47 -#define TVP7002_FBIT_F_0_START_L_OFF 0x48 -#define TVP7002_FBIT_F_1_START_L_OFF 0x49 -#define TVP7002_YUV_Y_G_COEF_LSBS 0x4a -#define TVP7002_YUV_Y_G_COEF_MSBS 0x4b -#define TVP7002_YUV_Y_B_COEF_LSBS 0x4c -#define TVP7002_YUV_Y_B_COEF_MSBS 0x4d -#define TVP7002_YUV_Y_R_COEF_LSBS 0x4e -#define TVP7002_YUV_Y_R_COEF_MSBS 0x4f -#define TVP7002_YUV_U_G_COEF_LSBS 0x50 -#define TVP7002_YUV_U_G_COEF_MSBS 0x51 -#define TVP7002_YUV_U_B_COEF_LSBS 0x52 -#define TVP7002_YUV_U_B_COEF_MSBS 0x53 -#define TVP7002_YUV_U_R_COEF_LSBS 0x54 -#define TVP7002_YUV_U_R_COEF_MSBS 0x55 -#define TVP7002_YUV_V_G_COEF_LSBS 0x56 -#define TVP7002_YUV_V_G_COEF_MSBS 0x57 -#define TVP7002_YUV_V_B_COEF_LSBS 0x58 -#define TVP7002_YUV_V_B_COEF_MSBS 0x59 -#define TVP7002_YUV_V_R_COEF_LSBS 0x5a -#define TVP7002_YUV_V_R_COEF_MSBS 0x5b - diff --git a/drivers/media/video/upd64031a.c b/drivers/media/video/upd64031a.c deleted file mode 100644 index 1e744654209..00000000000 --- a/drivers/media/video/upd64031a.c +++ /dev/null @@ -1,274 +0,0 @@ -/* - * upd64031A - NEC Electronics Ghost Reduction for NTSC in Japan - * - * 2003 by T.Adachi - * 2003 by Takeru KOMORIYA - * 2006 by Hans Verkuil - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -/* --------------------- read registers functions define -------------------- */ - -/* bit masks */ -#define GR_MODE_MASK 0xc0 -#define DIRECT_3DYCS_CONNECT_MASK 0xc0 -#define SYNC_CIRCUIT_MASK 0xa0 - -/* -------------------------------------------------------------------------- */ - -MODULE_DESCRIPTION("uPD64031A driver"); -MODULE_AUTHOR("T. Adachi, Takeru KOMORIYA, Hans Verkuil"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0644); - -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - - -enum { - R00 = 0, R01, R02, R03, R04, - R05, R06, R07, R08, R09, - R0A, R0B, R0C, R0D, R0E, R0F, - /* unused registers - R10, R11, R12, R13, R14, - R15, R16, R17, - */ - TOT_REGS -}; - -struct upd64031a_state { - struct v4l2_subdev sd; - u8 regs[TOT_REGS]; - u8 gr_mode; - u8 direct_3dycs_connect; - u8 ext_comp_sync; - u8 ext_vert_sync; -}; - -static inline struct upd64031a_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct upd64031a_state, sd); -} - -static u8 upd64031a_init[] = { - 0x00, 0xb8, 0x48, 0xd2, 0xe6, - 0x03, 0x10, 0x0b, 0xaf, 0x7f, - 0x00, 0x00, 0x1d, 0x5e, 0x00, - 0xd0 -}; - -/* ------------------------------------------------------------------------ */ - -static u8 upd64031a_read(struct v4l2_subdev *sd, u8 reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 buf[2]; - - if (reg >= sizeof(buf)) - return 0xff; - i2c_master_recv(client, buf, 2); - return buf[reg]; -} - -/* ------------------------------------------------------------------------ */ - -static void upd64031a_write(struct v4l2_subdev *sd, u8 reg, u8 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 buf[2]; - - buf[0] = reg; - buf[1] = val; - v4l2_dbg(1, debug, sd, "write reg: %02X val: %02X\n", reg, val); - if (i2c_master_send(client, buf, 2) != 2) - v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n", reg, val); -} - -/* ------------------------------------------------------------------------ */ - -/* The input changed due to new input or channel changed */ -static int upd64031a_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) -{ - struct upd64031a_state *state = to_state(sd); - u8 reg = state->regs[R00]; - - v4l2_dbg(1, debug, sd, "changed input or channel\n"); - upd64031a_write(sd, R00, reg | 0x10); - upd64031a_write(sd, R00, reg & ~0x10); - return 0; -} - -/* ------------------------------------------------------------------------ */ - -static int upd64031a_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct upd64031a_state *state = to_state(sd); - u8 r00, r05, r08; - - state->gr_mode = (input & 3) << 6; - state->direct_3dycs_connect = (input & 0xc) << 4; - state->ext_comp_sync = - (input & UPD64031A_COMPOSITE_EXTERNAL) << 1; - state->ext_vert_sync = - (input & UPD64031A_VERTICAL_EXTERNAL) << 2; - r00 = (state->regs[R00] & ~GR_MODE_MASK) | state->gr_mode; - r05 = (state->regs[R00] & ~SYNC_CIRCUIT_MASK) | - state->ext_comp_sync | state->ext_vert_sync; - r08 = (state->regs[R08] & ~DIRECT_3DYCS_CONNECT_MASK) | - state->direct_3dycs_connect; - upd64031a_write(sd, R00, r00); - upd64031a_write(sd, R05, r05); - upd64031a_write(sd, R08, r08); - return upd64031a_s_frequency(sd, NULL); -} - -static int upd64031a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_UPD64031A, 0); -} - -static int upd64031a_log_status(struct v4l2_subdev *sd) -{ - v4l2_info(sd, "Status: SA00=0x%02x SA01=0x%02x\n", - upd64031a_read(sd, 0), upd64031a_read(sd, 1)); - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int upd64031a_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - reg->val = upd64031a_read(sd, reg->reg & 0xff); - reg->size = 1; - return 0; -} - -static int upd64031a_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - upd64031a_write(sd, reg->reg & 0xff, reg->val & 0xff); - return 0; -} -#endif - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops upd64031a_core_ops = { - .log_status = upd64031a_log_status, - .g_chip_ident = upd64031a_g_chip_ident, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = upd64031a_g_register, - .s_register = upd64031a_s_register, -#endif -}; - -static const struct v4l2_subdev_tuner_ops upd64031a_tuner_ops = { - .s_frequency = upd64031a_s_frequency, -}; - -static const struct v4l2_subdev_video_ops upd64031a_video_ops = { - .s_routing = upd64031a_s_routing, -}; - -static const struct v4l2_subdev_ops upd64031a_ops = { - .core = &upd64031a_core_ops, - .tuner = &upd64031a_tuner_ops, - .video = &upd64031a_video_ops, -}; - -/* ------------------------------------------------------------------------ */ - -/* i2c implementation */ - -static int upd64031a_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct upd64031a_state *state; - struct v4l2_subdev *sd; - int i; - - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - state = kzalloc(sizeof(struct upd64031a_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &upd64031a_ops); - memcpy(state->regs, upd64031a_init, sizeof(state->regs)); - state->gr_mode = UPD64031A_GR_ON << 6; - state->direct_3dycs_connect = UPD64031A_3DYCS_COMPOSITE << 4; - state->ext_comp_sync = state->ext_vert_sync = 0; - for (i = 0; i < TOT_REGS; i++) - upd64031a_write(sd, i, state->regs[i]); - return 0; -} - -static int upd64031a_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 upd64031a_id[] = { - { "upd64031a", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, upd64031a_id); - -static struct i2c_driver upd64031a_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "upd64031a", - }, - .probe = upd64031a_probe, - .remove = upd64031a_remove, - .id_table = upd64031a_id, -}; - -module_i2c_driver(upd64031a_driver); diff --git a/drivers/media/video/upd64083.c b/drivers/media/video/upd64083.c deleted file mode 100644 index 75d6acc6201..00000000000 --- a/drivers/media/video/upd64083.c +++ /dev/null @@ -1,246 +0,0 @@ -/* - * upd6408x - NEC Electronics 3-Dimensional Y/C separation driver - * - * 2003 by T.Adachi (tadachi@tadachi-net.com) - * 2003 by Takeru KOMORIYA - * 2006 by Hans Verkuil - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("uPD64083 driver"); -MODULE_AUTHOR("T. Adachi, Takeru KOMORIYA, Hans Verkuil"); -MODULE_LICENSE("GPL"); - -static bool debug; -module_param(debug, bool, 0644); - -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - - -enum { - R00 = 0, R01, R02, R03, R04, - R05, R06, R07, R08, R09, - R0A, R0B, R0C, R0D, R0E, R0F, - R10, R11, R12, R13, R14, - R15, R16, - TOT_REGS -}; - -struct upd64083_state { - struct v4l2_subdev sd; - u8 mode; - u8 ext_y_adc; - u8 regs[TOT_REGS]; -}; - -static inline struct upd64083_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct upd64083_state, sd); -} - -/* Initial values when used in combination with the - NEC upd64031a ghost reduction chip. */ -static u8 upd64083_init[] = { - 0x1f, 0x01, 0xa0, 0x2d, 0x29, /* we use EXCSS=0 */ - 0x36, 0xdd, 0x05, 0x56, 0x48, - 0x00, 0x3a, 0xa0, 0x05, 0x08, - 0x44, 0x60, 0x08, 0x52, 0xf8, - 0x53, 0x60, 0x10 -}; - -/* ------------------------------------------------------------------------ */ - -static void upd64083_write(struct v4l2_subdev *sd, u8 reg, u8 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 buf[2]; - - buf[0] = reg; - buf[1] = val; - v4l2_dbg(1, debug, sd, "write reg: %02x val: %02x\n", reg, val); - if (i2c_master_send(client, buf, 2) != 2) - v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n", reg, val); -} - -/* ------------------------------------------------------------------------ */ - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static u8 upd64083_read(struct v4l2_subdev *sd, u8 reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 buf[7]; - - if (reg >= sizeof(buf)) - return 0xff; - i2c_master_recv(client, buf, sizeof(buf)); - return buf[reg]; -} -#endif - -/* ------------------------------------------------------------------------ */ - -static int upd64083_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct upd64083_state *state = to_state(sd); - u8 r00, r02; - - if (input > 7 || (input & 6) == 6) - return -EINVAL; - state->mode = (input & 3) << 6; - state->ext_y_adc = (input & UPD64083_EXT_Y_ADC) << 3; - r00 = (state->regs[R00] & ~(3 << 6)) | state->mode; - r02 = (state->regs[R02] & ~(1 << 5)) | state->ext_y_adc; - upd64083_write(sd, R00, r00); - upd64083_write(sd, R02, r02); - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int upd64083_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - reg->val = upd64083_read(sd, reg->reg & 0xff); - reg->size = 1; - return 0; -} - -static int upd64083_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - upd64083_write(sd, reg->reg & 0xff, reg->val & 0xff); - return 0; -} -#endif - -static int upd64083_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_UPD64083, 0); -} - -static int upd64083_log_status(struct v4l2_subdev *sd) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 buf[7]; - - i2c_master_recv(client, buf, 7); - v4l2_info(sd, "Status: SA00=%02x SA01=%02x SA02=%02x SA03=%02x " - "SA04=%02x SA05=%02x SA06=%02x\n", - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops upd64083_core_ops = { - .log_status = upd64083_log_status, - .g_chip_ident = upd64083_g_chip_ident, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = upd64083_g_register, - .s_register = upd64083_s_register, -#endif -}; - -static const struct v4l2_subdev_video_ops upd64083_video_ops = { - .s_routing = upd64083_s_routing, -}; - -static const struct v4l2_subdev_ops upd64083_ops = { - .core = &upd64083_core_ops, - .video = &upd64083_video_ops, -}; - -/* ------------------------------------------------------------------------ */ - -/* i2c implementation */ - -static int upd64083_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct upd64083_state *state; - struct v4l2_subdev *sd; - int i; - - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - state = kzalloc(sizeof(struct upd64083_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &upd64083_ops); - /* Initially assume that a ghost reduction chip is present */ - state->mode = 0; /* YCS mode */ - state->ext_y_adc = (1 << 5); - memcpy(state->regs, upd64083_init, TOT_REGS); - for (i = 0; i < TOT_REGS; i++) - upd64083_write(sd, i, state->regs[i]); - return 0; -} - -static int upd64083_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 upd64083_id[] = { - { "upd64083", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, upd64083_id); - -static struct i2c_driver upd64083_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "upd64083", - }, - .probe = upd64083_probe, - .remove = upd64083_remove, - .id_table = upd64083_id, -}; - -module_i2c_driver(upd64083_driver); diff --git a/drivers/media/video/vp27smpx.c b/drivers/media/video/vp27smpx.c deleted file mode 100644 index 7cfbc9d94a4..00000000000 --- a/drivers/media/video/vp27smpx.c +++ /dev/null @@ -1,211 +0,0 @@ -/* - * vp27smpx - driver version 0.0.1 - * - * Copyright (C) 2007 Hans Verkuil - * - * Based on a tvaudio patch from Takahiro Adachi - * and Kazuhiko Kawakami - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("vp27smpx driver"); -MODULE_AUTHOR("Hans Verkuil"); -MODULE_LICENSE("GPL"); - - -/* ----------------------------------------------------------------------- */ - -struct vp27smpx_state { - struct v4l2_subdev sd; - int radio; - u32 audmode; -}; - -static inline struct vp27smpx_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct vp27smpx_state, sd); -} - -static void vp27smpx_set_audmode(struct v4l2_subdev *sd, u32 audmode) -{ - struct vp27smpx_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 data[3] = { 0x00, 0x00, 0x04 }; - - switch (audmode) { - case V4L2_TUNER_MODE_MONO: - case V4L2_TUNER_MODE_LANG1: - break; - case V4L2_TUNER_MODE_STEREO: - case V4L2_TUNER_MODE_LANG1_LANG2: - data[1] = 0x01; - break; - case V4L2_TUNER_MODE_LANG2: - data[1] = 0x02; - break; - } - - if (i2c_master_send(client, data, sizeof(data)) != sizeof(data)) - v4l2_err(sd, "I/O error setting audmode\n"); - else - state->audmode = audmode; -} - -static int vp27smpx_s_radio(struct v4l2_subdev *sd) -{ - struct vp27smpx_state *state = to_state(sd); - - state->radio = 1; - return 0; -} - -static int vp27smpx_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) -{ - struct vp27smpx_state *state = to_state(sd); - - state->radio = 0; - return 0; -} - -static int vp27smpx_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct vp27smpx_state *state = to_state(sd); - - if (!state->radio) - vp27smpx_set_audmode(sd, vt->audmode); - return 0; -} - -static int vp27smpx_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct vp27smpx_state *state = to_state(sd); - - if (state->radio) - return 0; - vt->audmode = state->audmode; - vt->capability = V4L2_TUNER_CAP_STEREO | - V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; - vt->rxsubchans = V4L2_TUNER_SUB_MONO; - return 0; -} - -static int vp27smpx_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VP27SMPX, 0); -} - -static int vp27smpx_log_status(struct v4l2_subdev *sd) -{ - struct vp27smpx_state *state = to_state(sd); - - v4l2_info(sd, "Audio Mode: %u%s\n", state->audmode, - state->radio ? " (Radio)" : ""); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops vp27smpx_core_ops = { - .log_status = vp27smpx_log_status, - .g_chip_ident = vp27smpx_g_chip_ident, - .s_std = vp27smpx_s_std, -}; - -static const struct v4l2_subdev_tuner_ops vp27smpx_tuner_ops = { - .s_radio = vp27smpx_s_radio, - .s_tuner = vp27smpx_s_tuner, - .g_tuner = vp27smpx_g_tuner, -}; - -static const struct v4l2_subdev_ops vp27smpx_ops = { - .core = &vp27smpx_core_ops, - .tuner = &vp27smpx_tuner_ops, -}; - -/* ----------------------------------------------------------------------- */ - -/* i2c implementation */ - -/* - * Generic i2c probe - * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' - */ - -static int vp27smpx_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct vp27smpx_state *state; - struct v4l2_subdev *sd; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - state = kzalloc(sizeof(struct vp27smpx_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &vp27smpx_ops); - state->audmode = V4L2_TUNER_MODE_STEREO; - - /* initialize vp27smpx */ - vp27smpx_set_audmode(sd, state->audmode); - return 0; -} - -static int vp27smpx_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 vp27smpx_id[] = { - { "vp27smpx", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, vp27smpx_id); - -static struct i2c_driver vp27smpx_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "vp27smpx", - }, - .probe = vp27smpx_probe, - .remove = vp27smpx_remove, - .id_table = vp27smpx_id, -}; - -module_i2c_driver(vp27smpx_driver); diff --git a/drivers/media/video/vpx3220.c b/drivers/media/video/vpx3220.c deleted file mode 100644 index 2f67b4c5c82..00000000000 --- a/drivers/media/video/vpx3220.c +++ /dev/null @@ -1,591 +0,0 @@ -/* - * vpx3220a, vpx3216b & vpx3214c video decoder driver version 0.0.1 - * - * Copyright (C) 2001 Laurent Pinchart - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video decoder driver"); -MODULE_AUTHOR("Laurent Pinchart"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - - -#define VPX_TIMEOUT_COUNT 10 - -/* ----------------------------------------------------------------------- */ - -struct vpx3220 { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - unsigned char reg[255]; - - v4l2_std_id norm; - int ident; - int input; - int enable; -}; - -static inline struct vpx3220 *to_vpx3220(struct v4l2_subdev *sd) -{ - return container_of(sd, struct vpx3220, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct vpx3220, hdl)->sd; -} - -static char *inputs[] = { "internal", "composite", "svideo" }; - -/* ----------------------------------------------------------------------- */ - -static inline int vpx3220_write(struct v4l2_subdev *sd, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct vpx3220 *decoder = i2c_get_clientdata(client); - - decoder->reg[reg] = value; - return i2c_smbus_write_byte_data(client, reg, value); -} - -static inline int vpx3220_read(struct v4l2_subdev *sd, u8 reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_read_byte_data(client, reg); -} - -static int vpx3220_fp_status(struct v4l2_subdev *sd) -{ - unsigned char status; - unsigned int i; - - for (i = 0; i < VPX_TIMEOUT_COUNT; i++) { - status = vpx3220_read(sd, 0x29); - - if (!(status & 4)) - return 0; - - udelay(10); - - if (need_resched()) - cond_resched(); - } - - return -1; -} - -static int vpx3220_fp_write(struct v4l2_subdev *sd, u8 fpaddr, u16 data) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - /* Write the 16-bit address to the FPWR register */ - if (i2c_smbus_write_word_data(client, 0x27, swab16(fpaddr)) == -1) { - v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); - return -1; - } - - if (vpx3220_fp_status(sd) < 0) - return -1; - - /* Write the 16-bit data to the FPDAT register */ - if (i2c_smbus_write_word_data(client, 0x28, swab16(data)) == -1) { - v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); - return -1; - } - - return 0; -} - -static u16 vpx3220_fp_read(struct v4l2_subdev *sd, u16 fpaddr) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - s16 data; - - /* Write the 16-bit address to the FPRD register */ - if (i2c_smbus_write_word_data(client, 0x26, swab16(fpaddr)) == -1) { - v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); - return -1; - } - - if (vpx3220_fp_status(sd) < 0) - return -1; - - /* Read the 16-bit data from the FPDAT register */ - data = i2c_smbus_read_word_data(client, 0x28); - if (data == -1) { - v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); - return -1; - } - - return swab16(data); -} - -static int vpx3220_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len) -{ - u8 reg; - int ret = -1; - - while (len >= 2) { - reg = *data++; - ret = vpx3220_write(sd, reg, *data++); - if (ret < 0) - break; - len -= 2; - } - - return ret; -} - -static int vpx3220_write_fp_block(struct v4l2_subdev *sd, - const u16 *data, unsigned int len) -{ - u8 reg; - int ret = 0; - - while (len > 1) { - reg = *data++; - ret |= vpx3220_fp_write(sd, reg, *data++); - len -= 2; - } - - return ret; -} - -/* ---------------------------------------------------------------------- */ - -static const unsigned short init_ntsc[] = { - 0x1c, 0x00, /* NTSC tint angle */ - 0x88, 17, /* Window 1 vertical */ - 0x89, 240, /* Vertical lines in */ - 0x8a, 240, /* Vertical lines out */ - 0x8b, 000, /* Horizontal begin */ - 0x8c, 640, /* Horizontal length */ - 0x8d, 640, /* Number of pixels */ - 0x8f, 0xc00, /* Disable window 2 */ - 0xf0, 0x73, /* 13.5 MHz transport, Forced - * mode, latch windows */ - 0xf2, 0x13, /* NTSC M, composite input */ - 0xe7, 0x1e1, /* Enable vertical standard - * locking @ 240 lines */ -}; - -static const unsigned short init_pal[] = { - 0x88, 23, /* Window 1 vertical begin */ - 0x89, 288, /* Vertical lines in (16 lines - * skipped by the VFE) */ - 0x8a, 288, /* Vertical lines out (16 lines - * skipped by the VFE) */ - 0x8b, 16, /* Horizontal begin */ - 0x8c, 768, /* Horizontal length */ - 0x8d, 784, /* Number of pixels - * Must be >= Horizontal begin + Horizontal length */ - 0x8f, 0xc00, /* Disable window 2 */ - 0xf0, 0x77, /* 13.5 MHz transport, Forced - * mode, latch windows */ - 0xf2, 0x3d1, /* PAL B,G,H,I, composite input */ - 0xe7, 0x241, /* PAL/SECAM set to 288 lines */ -}; - -static const unsigned short init_secam[] = { - 0x88, 23, /* Window 1 vertical begin */ - 0x89, 288, /* Vertical lines in (16 lines - * skipped by the VFE) */ - 0x8a, 288, /* Vertical lines out (16 lines - * skipped by the VFE) */ - 0x8b, 16, /* Horizontal begin */ - 0x8c, 768, /* Horizontal length */ - 0x8d, 784, /* Number of pixels - * Must be >= Horizontal begin + Horizontal length */ - 0x8f, 0xc00, /* Disable window 2 */ - 0xf0, 0x77, /* 13.5 MHz transport, Forced - * mode, latch windows */ - 0xf2, 0x3d5, /* SECAM, composite input */ - 0xe7, 0x241, /* PAL/SECAM set to 288 lines */ -}; - -static const unsigned char init_common[] = { - 0xf2, 0x00, /* Disable all outputs */ - 0x33, 0x0d, /* Luma : VIN2, Chroma : CIN - * (clamp off) */ - 0xd8, 0xa8, /* HREF/VREF active high, VREF - * pulse = 2, Odd/Even flag */ - 0x20, 0x03, /* IF compensation 0dB/oct */ - 0xe0, 0xff, /* Open up all comparators */ - 0xe1, 0x00, - 0xe2, 0x7f, - 0xe3, 0x80, - 0xe4, 0x7f, - 0xe5, 0x80, - 0xe6, 0x00, /* Brightness set to 0 */ - 0xe7, 0xe0, /* Contrast to 1.0, noise shaping - * 10 to 8 2-bit error diffusion */ - 0xe8, 0xf8, /* YUV422, CbCr binary offset, - * ... (p.32) */ - 0xea, 0x18, /* LLC2 connected, output FIFO - * reset with VACTintern */ - 0xf0, 0x8a, /* Half full level to 10, bus - * shuffler [7:0, 23:16, 15:8] */ - 0xf1, 0x18, /* Single clock, sync mode, no - * FE delay, no HLEN counter */ - 0xf8, 0x12, /* Port A, PIXCLK, HF# & FE# - * strength to 2 */ - 0xf9, 0x24, /* Port B, HREF, VREF, PREF & - * ALPHA strength to 4 */ -}; - -static const unsigned short init_fp[] = { - 0x59, 0, - 0xa0, 2070, /* ACC reference */ - 0xa3, 0, - 0xa4, 0, - 0xa8, 30, - 0xb2, 768, - 0xbe, 27, - 0x58, 0, - 0x26, 0, - 0x4b, 0x298, /* PLL gain */ -}; - - -static int vpx3220_init(struct v4l2_subdev *sd, u32 val) -{ - struct vpx3220 *decoder = to_vpx3220(sd); - - vpx3220_write_block(sd, init_common, sizeof(init_common)); - vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1); - if (decoder->norm & V4L2_STD_NTSC) - vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1); - else if (decoder->norm & V4L2_STD_PAL) - vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); - else if (decoder->norm & V4L2_STD_SECAM) - vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1); - else - vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); - return 0; -} - -static int vpx3220_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd) -{ - int res = V4L2_IN_ST_NO_SIGNAL, status; - v4l2_std_id std = 0; - - status = vpx3220_fp_read(sd, 0x0f3); - - v4l2_dbg(1, debug, sd, "status: 0x%04x\n", status); - - if (status < 0) - return status; - - if ((status & 0x20) == 0) { - res = 0; - - switch (status & 0x18) { - case 0x00: - case 0x10: - case 0x14: - case 0x18: - std = V4L2_STD_PAL; - break; - - case 0x08: - std = V4L2_STD_SECAM; - break; - - case 0x04: - case 0x0c: - case 0x1c: - std = V4L2_STD_NTSC; - break; - } - } - if (pstd) - *pstd = std; - if (pstatus) - *pstatus = res; - return 0; -} - -static int vpx3220_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) -{ - v4l2_dbg(1, debug, sd, "querystd\n"); - return vpx3220_status(sd, NULL, std); -} - -static int vpx3220_g_input_status(struct v4l2_subdev *sd, u32 *status) -{ - v4l2_dbg(1, debug, sd, "g_input_status\n"); - return vpx3220_status(sd, status, NULL); -} - -static int vpx3220_s_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct vpx3220 *decoder = to_vpx3220(sd); - int temp_input; - - /* Here we back up the input selection because it gets - overwritten when we fill the registers with the - chosen video norm */ - temp_input = vpx3220_fp_read(sd, 0xf2); - - v4l2_dbg(1, debug, sd, "s_std %llx\n", (unsigned long long)std); - if (std & V4L2_STD_NTSC) { - vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1); - v4l2_dbg(1, debug, sd, "norm switched to NTSC\n"); - } else if (std & V4L2_STD_PAL) { - vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); - v4l2_dbg(1, debug, sd, "norm switched to PAL\n"); - } else if (std & V4L2_STD_SECAM) { - vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1); - v4l2_dbg(1, debug, sd, "norm switched to SECAM\n"); - } else { - return -EINVAL; - } - - decoder->norm = std; - - /* And here we set the backed up video input again */ - vpx3220_fp_write(sd, 0xf2, temp_input | 0x0010); - udelay(10); - return 0; -} - -static int vpx3220_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - int data; - - /* RJ: input = 0: ST8 (PCTV) input - input = 1: COMPOSITE input - input = 2: SVHS input */ - - const int input_vals[3][2] = { - {0x0c, 0}, - {0x0d, 0}, - {0x0e, 1} - }; - - if (input > 2) - return -EINVAL; - - v4l2_dbg(1, debug, sd, "input switched to %s\n", inputs[input]); - - vpx3220_write(sd, 0x33, input_vals[input][0]); - - data = vpx3220_fp_read(sd, 0xf2) & ~(0x0020); - if (data < 0) - return data; - /* 0x0010 is required to latch the setting */ - vpx3220_fp_write(sd, 0xf2, - data | (input_vals[input][1] << 5) | 0x0010); - - udelay(10); - return 0; -} - -static int vpx3220_s_stream(struct v4l2_subdev *sd, int enable) -{ - v4l2_dbg(1, debug, sd, "s_stream %s\n", enable ? "on" : "off"); - - vpx3220_write(sd, 0xf2, (enable ? 0x1b : 0x00)); - return 0; -} - -static int vpx3220_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - vpx3220_write(sd, 0xe6, ctrl->val); - return 0; - case V4L2_CID_CONTRAST: - /* Bit 7 and 8 is for noise shaping */ - vpx3220_write(sd, 0xe7, ctrl->val + 192); - return 0; - case V4L2_CID_SATURATION: - vpx3220_fp_write(sd, 0xa0, ctrl->val); - return 0; - case V4L2_CID_HUE: - vpx3220_fp_write(sd, 0x1c, ctrl->val); - return 0; - } - return -EINVAL; -} - -static int vpx3220_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct vpx3220 *decoder = to_vpx3220(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, decoder->ident, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_ctrl_ops vpx3220_ctrl_ops = { - .s_ctrl = vpx3220_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops vpx3220_core_ops = { - .g_chip_ident = vpx3220_g_chip_ident, - .init = vpx3220_init, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, - .s_std = vpx3220_s_std, -}; - -static const struct v4l2_subdev_video_ops vpx3220_video_ops = { - .s_routing = vpx3220_s_routing, - .s_stream = vpx3220_s_stream, - .querystd = vpx3220_querystd, - .g_input_status = vpx3220_g_input_status, -}; - -static const struct v4l2_subdev_ops vpx3220_ops = { - .core = &vpx3220_core_ops, - .video = &vpx3220_video_ops, -}; - -/* ----------------------------------------------------------------------- - * Client management code - */ - -static int vpx3220_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct vpx3220 *decoder; - struct v4l2_subdev *sd; - const char *name = NULL; - u8 ver; - u16 pn; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) - return -ENODEV; - - decoder = kzalloc(sizeof(struct vpx3220), GFP_KERNEL); - if (decoder == NULL) - return -ENOMEM; - sd = &decoder->sd; - v4l2_i2c_subdev_init(sd, client, &vpx3220_ops); - decoder->norm = V4L2_STD_PAL; - decoder->input = 0; - decoder->enable = 1; - v4l2_ctrl_handler_init(&decoder->hdl, 4); - v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, - V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); - v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, - V4L2_CID_CONTRAST, 0, 63, 1, 32); - v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, - V4L2_CID_SATURATION, 0, 4095, 1, 2048); - v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, - V4L2_CID_HUE, -512, 511, 1, 0); - sd->ctrl_handler = &decoder->hdl; - if (decoder->hdl.error) { - int err = decoder->hdl.error; - - v4l2_ctrl_handler_free(&decoder->hdl); - kfree(decoder); - return err; - } - v4l2_ctrl_handler_setup(&decoder->hdl); - - ver = i2c_smbus_read_byte_data(client, 0x00); - pn = (i2c_smbus_read_byte_data(client, 0x02) << 8) + - i2c_smbus_read_byte_data(client, 0x01); - decoder->ident = V4L2_IDENT_VPX3220A; - if (ver == 0xec) { - switch (pn) { - case 0x4680: - name = "vpx3220a"; - break; - case 0x4260: - name = "vpx3216b"; - decoder->ident = V4L2_IDENT_VPX3216B; - break; - case 0x4280: - name = "vpx3214c"; - decoder->ident = V4L2_IDENT_VPX3214C; - break; - } - } - if (name) - v4l2_info(sd, "%s found @ 0x%x (%s)\n", name, - client->addr << 1, client->adapter->name); - else - v4l2_info(sd, "chip (%02x:%04x) found @ 0x%x (%s)\n", - ver, pn, client->addr << 1, client->adapter->name); - - vpx3220_write_block(sd, init_common, sizeof(init_common)); - vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1); - /* Default to PAL */ - vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); - return 0; -} - -static int vpx3220_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct vpx3220 *decoder = to_vpx3220(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&decoder->hdl); - kfree(decoder); - return 0; -} - -static const struct i2c_device_id vpx3220_id[] = { - { "vpx3220a", 0 }, - { "vpx3216b", 0 }, - { "vpx3214c", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, vpx3220_id); - -static struct i2c_driver vpx3220_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "vpx3220", - }, - .probe = vpx3220_probe, - .remove = vpx3220_remove, - .id_table = vpx3220_id, -}; - -module_i2c_driver(vpx3220_driver); diff --git a/drivers/media/video/vs6624.c b/drivers/media/video/vs6624.c deleted file mode 100644 index 42ae9dc9c57..00000000000 --- a/drivers/media/video/vs6624.c +++ /dev/null @@ -1,928 +0,0 @@ -/* - * vs6624.c ST VS6624 CMOS image sensor driver - * - * Copyright (c) 2011 Analog Devices Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "vs6624_regs.h" - -#define VGA_WIDTH 640 -#define VGA_HEIGHT 480 -#define QVGA_WIDTH 320 -#define QVGA_HEIGHT 240 -#define QQVGA_WIDTH 160 -#define QQVGA_HEIGHT 120 -#define CIF_WIDTH 352 -#define CIF_HEIGHT 288 -#define QCIF_WIDTH 176 -#define QCIF_HEIGHT 144 -#define QQCIF_WIDTH 88 -#define QQCIF_HEIGHT 72 - -#define MAX_FRAME_RATE 30 - -struct vs6624 { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - struct v4l2_fract frame_rate; - struct v4l2_mbus_framefmt fmt; - unsigned ce_pin; -}; - -static const struct vs6624_format { - enum v4l2_mbus_pixelcode mbus_code; - enum v4l2_colorspace colorspace; -} vs6624_formats[] = { - { - .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - }, - { - .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - }, - { - .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE, - .colorspace = V4L2_COLORSPACE_SRGB, - }, -}; - -static struct v4l2_mbus_framefmt vs6624_default_fmt = { - .width = VGA_WIDTH, - .height = VGA_HEIGHT, - .code = V4L2_MBUS_FMT_UYVY8_2X8, - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_JPEG, -}; - -static const u16 vs6624_p1[] = { - 0x8104, 0x03, - 0x8105, 0x01, - 0xc900, 0x03, - 0xc904, 0x47, - 0xc905, 0x10, - 0xc906, 0x80, - 0xc907, 0x3a, - 0x903a, 0x02, - 0x903b, 0x47, - 0x903c, 0x15, - 0xc908, 0x31, - 0xc909, 0xdc, - 0xc90a, 0x80, - 0xc90b, 0x44, - 0x9044, 0x02, - 0x9045, 0x31, - 0x9046, 0xe2, - 0xc90c, 0x07, - 0xc90d, 0xe0, - 0xc90e, 0x80, - 0xc90f, 0x47, - 0x9047, 0x90, - 0x9048, 0x83, - 0x9049, 0x81, - 0x904a, 0xe0, - 0x904b, 0x60, - 0x904c, 0x08, - 0x904d, 0x90, - 0x904e, 0xc0, - 0x904f, 0x43, - 0x9050, 0x74, - 0x9051, 0x01, - 0x9052, 0xf0, - 0x9053, 0x80, - 0x9054, 0x05, - 0x9055, 0xE4, - 0x9056, 0x90, - 0x9057, 0xc0, - 0x9058, 0x43, - 0x9059, 0xf0, - 0x905a, 0x02, - 0x905b, 0x07, - 0x905c, 0xec, - 0xc910, 0x5d, - 0xc911, 0xca, - 0xc912, 0x80, - 0xc913, 0x5d, - 0x905d, 0xa3, - 0x905e, 0x04, - 0x905f, 0xf0, - 0x9060, 0xa3, - 0x9061, 0x04, - 0x9062, 0xf0, - 0x9063, 0x22, - 0xc914, 0x72, - 0xc915, 0x92, - 0xc916, 0x80, - 0xc917, 0x64, - 0x9064, 0x74, - 0x9065, 0x01, - 0x9066, 0x02, - 0x9067, 0x72, - 0x9068, 0x95, - 0xc918, 0x47, - 0xc919, 0xf2, - 0xc91a, 0x81, - 0xc91b, 0x69, - 0x9169, 0x74, - 0x916a, 0x02, - 0x916b, 0xf0, - 0x916c, 0xec, - 0x916d, 0xb4, - 0x916e, 0x10, - 0x916f, 0x0a, - 0x9170, 0x90, - 0x9171, 0x80, - 0x9172, 0x16, - 0x9173, 0xe0, - 0x9174, 0x70, - 0x9175, 0x04, - 0x9176, 0x90, - 0x9177, 0xd3, - 0x9178, 0xc4, - 0x9179, 0xf0, - 0x917a, 0x22, - 0xc91c, 0x0a, - 0xc91d, 0xbe, - 0xc91e, 0x80, - 0xc91f, 0x73, - 0x9073, 0xfc, - 0x9074, 0xa3, - 0x9075, 0xe0, - 0x9076, 0xf5, - 0x9077, 0x82, - 0x9078, 0x8c, - 0x9079, 0x83, - 0x907a, 0xa3, - 0x907b, 0xa3, - 0x907c, 0xe0, - 0x907d, 0xfc, - 0x907e, 0xa3, - 0x907f, 0xe0, - 0x9080, 0xc3, - 0x9081, 0x9f, - 0x9082, 0xff, - 0x9083, 0xec, - 0x9084, 0x9e, - 0x9085, 0xfe, - 0x9086, 0x02, - 0x9087, 0x0a, - 0x9088, 0xea, - 0xc920, 0x47, - 0xc921, 0x38, - 0xc922, 0x80, - 0xc923, 0x89, - 0x9089, 0xec, - 0x908a, 0xd3, - 0x908b, 0x94, - 0x908c, 0x20, - 0x908d, 0x40, - 0x908e, 0x01, - 0x908f, 0x1c, - 0x9090, 0x90, - 0x9091, 0xd3, - 0x9092, 0xd4, - 0x9093, 0xec, - 0x9094, 0xf0, - 0x9095, 0x02, - 0x9096, 0x47, - 0x9097, 0x3d, - 0xc924, 0x45, - 0xc925, 0xca, - 0xc926, 0x80, - 0xc927, 0x98, - 0x9098, 0x12, - 0x9099, 0x77, - 0x909a, 0xd6, - 0x909b, 0x02, - 0x909c, 0x45, - 0x909d, 0xcd, - 0xc928, 0x20, - 0xc929, 0xd5, - 0xc92a, 0x80, - 0xc92b, 0x9e, - 0x909e, 0x90, - 0x909f, 0x82, - 0x90a0, 0x18, - 0x90a1, 0xe0, - 0x90a2, 0xb4, - 0x90a3, 0x03, - 0x90a4, 0x0e, - 0x90a5, 0x90, - 0x90a6, 0x83, - 0x90a7, 0xbf, - 0x90a8, 0xe0, - 0x90a9, 0x60, - 0x90aa, 0x08, - 0x90ab, 0x90, - 0x90ac, 0x81, - 0x90ad, 0xfc, - 0x90ae, 0xe0, - 0x90af, 0xff, - 0x90b0, 0xc3, - 0x90b1, 0x13, - 0x90b2, 0xf0, - 0x90b3, 0x90, - 0x90b4, 0x81, - 0x90b5, 0xfc, - 0x90b6, 0xe0, - 0x90b7, 0xff, - 0x90b8, 0x02, - 0x90b9, 0x20, - 0x90ba, 0xda, - 0xc92c, 0x70, - 0xc92d, 0xbc, - 0xc92e, 0x80, - 0xc92f, 0xbb, - 0x90bb, 0x90, - 0x90bc, 0x82, - 0x90bd, 0x18, - 0x90be, 0xe0, - 0x90bf, 0xb4, - 0x90c0, 0x03, - 0x90c1, 0x06, - 0x90c2, 0x90, - 0x90c3, 0xc1, - 0x90c4, 0x06, - 0x90c5, 0x74, - 0x90c6, 0x05, - 0x90c7, 0xf0, - 0x90c8, 0x90, - 0x90c9, 0xd3, - 0x90ca, 0xa0, - 0x90cb, 0x02, - 0x90cc, 0x70, - 0x90cd, 0xbf, - 0xc930, 0x72, - 0xc931, 0x21, - 0xc932, 0x81, - 0xc933, 0x3b, - 0x913b, 0x7d, - 0x913c, 0x02, - 0x913d, 0x7f, - 0x913e, 0x7b, - 0x913f, 0x02, - 0x9140, 0x72, - 0x9141, 0x25, - 0xc934, 0x28, - 0xc935, 0xae, - 0xc936, 0x80, - 0xc937, 0xd2, - 0x90d2, 0xf0, - 0x90d3, 0x90, - 0x90d4, 0xd2, - 0x90d5, 0x0a, - 0x90d6, 0x02, - 0x90d7, 0x28, - 0x90d8, 0xb4, - 0xc938, 0x28, - 0xc939, 0xb1, - 0xc93a, 0x80, - 0xc93b, 0xd9, - 0x90d9, 0x90, - 0x90da, 0x83, - 0x90db, 0xba, - 0x90dc, 0xe0, - 0x90dd, 0xff, - 0x90de, 0x90, - 0x90df, 0xd2, - 0x90e0, 0x08, - 0x90e1, 0xe0, - 0x90e2, 0xe4, - 0x90e3, 0xef, - 0x90e4, 0xf0, - 0x90e5, 0xa3, - 0x90e6, 0xe0, - 0x90e7, 0x74, - 0x90e8, 0xff, - 0x90e9, 0xf0, - 0x90ea, 0x90, - 0x90eb, 0xd2, - 0x90ec, 0x0a, - 0x90ed, 0x02, - 0x90ee, 0x28, - 0x90ef, 0xb4, - 0xc93c, 0x29, - 0xc93d, 0x79, - 0xc93e, 0x80, - 0xc93f, 0xf0, - 0x90f0, 0xf0, - 0x90f1, 0x90, - 0x90f2, 0xd2, - 0x90f3, 0x0e, - 0x90f4, 0x02, - 0x90f5, 0x29, - 0x90f6, 0x7f, - 0xc940, 0x29, - 0xc941, 0x7c, - 0xc942, 0x80, - 0xc943, 0xf7, - 0x90f7, 0x90, - 0x90f8, 0x83, - 0x90f9, 0xba, - 0x90fa, 0xe0, - 0x90fb, 0xff, - 0x90fc, 0x90, - 0x90fd, 0xd2, - 0x90fe, 0x0c, - 0x90ff, 0xe0, - 0x9100, 0xe4, - 0x9101, 0xef, - 0x9102, 0xf0, - 0x9103, 0xa3, - 0x9104, 0xe0, - 0x9105, 0x74, - 0x9106, 0xff, - 0x9107, 0xf0, - 0x9108, 0x90, - 0x9109, 0xd2, - 0x910a, 0x0e, - 0x910b, 0x02, - 0x910c, 0x29, - 0x910d, 0x7f, - 0xc944, 0x2a, - 0xc945, 0x42, - 0xc946, 0x81, - 0xc947, 0x0e, - 0x910e, 0xf0, - 0x910f, 0x90, - 0x9110, 0xd2, - 0x9111, 0x12, - 0x9112, 0x02, - 0x9113, 0x2a, - 0x9114, 0x48, - 0xc948, 0x2a, - 0xc949, 0x45, - 0xc94a, 0x81, - 0xc94b, 0x15, - 0x9115, 0x90, - 0x9116, 0x83, - 0x9117, 0xba, - 0x9118, 0xe0, - 0x9119, 0xff, - 0x911a, 0x90, - 0x911b, 0xd2, - 0x911c, 0x10, - 0x911d, 0xe0, - 0x911e, 0xe4, - 0x911f, 0xef, - 0x9120, 0xf0, - 0x9121, 0xa3, - 0x9122, 0xe0, - 0x9123, 0x74, - 0x9124, 0xff, - 0x9125, 0xf0, - 0x9126, 0x90, - 0x9127, 0xd2, - 0x9128, 0x12, - 0x9129, 0x02, - 0x912a, 0x2a, - 0x912b, 0x48, - 0xc900, 0x01, - 0x0000, 0x00, -}; - -static const u16 vs6624_p2[] = { - 0x806f, 0x01, - 0x058c, 0x01, - 0x0000, 0x00, -}; - -static const u16 vs6624_run_setup[] = { - 0x1d18, 0x00, /* Enableconstrainedwhitebalance */ - VS6624_PEAK_MIN_OUT_G_MSB, 0x3c, /* Damper PeakGain Output MSB */ - VS6624_PEAK_MIN_OUT_G_LSB, 0x66, /* Damper PeakGain Output LSB */ - VS6624_CM_LOW_THR_MSB, 0x65, /* Damper Low MSB */ - VS6624_CM_LOW_THR_LSB, 0xd1, /* Damper Low LSB */ - VS6624_CM_HIGH_THR_MSB, 0x66, /* Damper High MSB */ - VS6624_CM_HIGH_THR_LSB, 0x62, /* Damper High LSB */ - VS6624_CM_MIN_OUT_MSB, 0x00, /* Damper Min output MSB */ - VS6624_CM_MIN_OUT_LSB, 0x00, /* Damper Min output LSB */ - VS6624_NORA_DISABLE, 0x00, /* Nora fDisable */ - VS6624_NORA_USAGE, 0x04, /* Nora usage */ - VS6624_NORA_LOW_THR_MSB, 0x63, /* Damper Low MSB Changed 0x63 to 0x65 */ - VS6624_NORA_LOW_THR_LSB, 0xd1, /* Damper Low LSB */ - VS6624_NORA_HIGH_THR_MSB, 0x68, /* Damper High MSB */ - VS6624_NORA_HIGH_THR_LSB, 0xdd, /* Damper High LSB */ - VS6624_NORA_MIN_OUT_MSB, 0x3a, /* Damper Min output MSB */ - VS6624_NORA_MIN_OUT_LSB, 0x00, /* Damper Min output LSB */ - VS6624_F2B_DISABLE, 0x00, /* Disable */ - 0x1d8a, 0x30, /* MAXWeightHigh */ - 0x1d91, 0x62, /* fpDamperLowThresholdHigh MSB */ - 0x1d92, 0x4a, /* fpDamperLowThresholdHigh LSB */ - 0x1d95, 0x65, /* fpDamperHighThresholdHigh MSB */ - 0x1d96, 0x0e, /* fpDamperHighThresholdHigh LSB */ - 0x1da1, 0x3a, /* fpMinimumDamperOutputLow MSB */ - 0x1da2, 0xb8, /* fpMinimumDamperOutputLow LSB */ - 0x1e08, 0x06, /* MAXWeightLow */ - 0x1e0a, 0x0a, /* MAXWeightHigh */ - 0x1601, 0x3a, /* Red A MSB */ - 0x1602, 0x14, /* Red A LSB */ - 0x1605, 0x3b, /* Blue A MSB */ - 0x1606, 0x85, /* BLue A LSB */ - 0x1609, 0x3b, /* RED B MSB */ - 0x160a, 0x85, /* RED B LSB */ - 0x160d, 0x3a, /* Blue B MSB */ - 0x160e, 0x14, /* Blue B LSB */ - 0x1611, 0x30, /* Max Distance from Locus MSB */ - 0x1612, 0x8f, /* Max Distance from Locus MSB */ - 0x1614, 0x01, /* Enable constrainer */ - 0x0000, 0x00, -}; - -static const u16 vs6624_default[] = { - VS6624_CONTRAST0, 0x84, - VS6624_SATURATION0, 0x75, - VS6624_GAMMA0, 0x11, - VS6624_CONTRAST1, 0x84, - VS6624_SATURATION1, 0x75, - VS6624_GAMMA1, 0x11, - VS6624_MAN_RG, 0x80, - VS6624_MAN_GG, 0x80, - VS6624_MAN_BG, 0x80, - VS6624_WB_MODE, 0x1, - VS6624_EXPO_COMPENSATION, 0xfe, - VS6624_EXPO_METER, 0x0, - VS6624_LIGHT_FREQ, 0x64, - VS6624_PEAK_GAIN, 0xe, - VS6624_PEAK_LOW_THR, 0x28, - VS6624_HMIRROR0, 0x0, - VS6624_VFLIP0, 0x0, - VS6624_ZOOM_HSTEP0_MSB, 0x0, - VS6624_ZOOM_HSTEP0_LSB, 0x1, - VS6624_ZOOM_VSTEP0_MSB, 0x0, - VS6624_ZOOM_VSTEP0_LSB, 0x1, - VS6624_PAN_HSTEP0_MSB, 0x0, - VS6624_PAN_HSTEP0_LSB, 0xf, - VS6624_PAN_VSTEP0_MSB, 0x0, - VS6624_PAN_VSTEP0_LSB, 0xf, - VS6624_SENSOR_MODE, 0x1, - VS6624_SYNC_CODE_SETUP, 0x21, - VS6624_DISABLE_FR_DAMPER, 0x0, - VS6624_FR_DEN, 0x1, - VS6624_FR_NUM_LSB, 0xf, - VS6624_INIT_PIPE_SETUP, 0x0, - VS6624_IMG_FMT0, 0x0, - VS6624_YUV_SETUP, 0x1, - VS6624_IMAGE_SIZE0, 0x2, - 0x0000, 0x00, -}; - -static inline struct vs6624 *to_vs6624(struct v4l2_subdev *sd) -{ - return container_of(sd, struct vs6624, sd); -} -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct vs6624, hdl)->sd; -} - -static int vs6624_read(struct v4l2_subdev *sd, u16 index) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 buf[2]; - - buf[0] = index >> 8; - buf[1] = index; - i2c_master_send(client, buf, 2); - i2c_master_recv(client, buf, 1); - - return buf[0]; -} - -static int vs6624_write(struct v4l2_subdev *sd, u16 index, - u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 buf[3]; - - buf[0] = index >> 8; - buf[1] = index; - buf[2] = value; - - return i2c_master_send(client, buf, 3); -} - -static int vs6624_writeregs(struct v4l2_subdev *sd, const u16 *regs) -{ - u16 reg; - u8 data; - - while (*regs != 0x00) { - reg = *regs++; - data = *regs++; - - vs6624_write(sd, reg, data); - } - return 0; -} - -static int vs6624_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - - switch (ctrl->id) { - case V4L2_CID_CONTRAST: - vs6624_write(sd, VS6624_CONTRAST0, ctrl->val); - break; - case V4L2_CID_SATURATION: - vs6624_write(sd, VS6624_SATURATION0, ctrl->val); - break; - case V4L2_CID_HFLIP: - vs6624_write(sd, VS6624_HMIRROR0, ctrl->val); - break; - case V4L2_CID_VFLIP: - vs6624_write(sd, VS6624_VFLIP0, ctrl->val); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int vs6624_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, - enum v4l2_mbus_pixelcode *code) -{ - if (index >= ARRAY_SIZE(vs6624_formats)) - return -EINVAL; - - *code = vs6624_formats[index].mbus_code; - return 0; -} - -static int vs6624_try_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) -{ - int index; - - for (index = 0; index < ARRAY_SIZE(vs6624_formats); index++) - if (vs6624_formats[index].mbus_code == fmt->code) - break; - if (index >= ARRAY_SIZE(vs6624_formats)) { - /* default to first format */ - index = 0; - fmt->code = vs6624_formats[0].mbus_code; - } - - /* sensor mode is VGA */ - if (fmt->width > VGA_WIDTH) - fmt->width = VGA_WIDTH; - if (fmt->height > VGA_HEIGHT) - fmt->height = VGA_HEIGHT; - fmt->width = fmt->width & (~3); - fmt->height = fmt->height & (~3); - fmt->field = V4L2_FIELD_NONE; - fmt->colorspace = vs6624_formats[index].colorspace; - return 0; -} - -static int vs6624_s_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) -{ - struct vs6624 *sensor = to_vs6624(sd); - int ret; - - ret = vs6624_try_mbus_fmt(sd, fmt); - if (ret) - return ret; - - /* set image format */ - switch (fmt->code) { - case V4L2_MBUS_FMT_UYVY8_2X8: - vs6624_write(sd, VS6624_IMG_FMT0, 0x0); - vs6624_write(sd, VS6624_YUV_SETUP, 0x1); - break; - case V4L2_MBUS_FMT_YUYV8_2X8: - vs6624_write(sd, VS6624_IMG_FMT0, 0x0); - vs6624_write(sd, VS6624_YUV_SETUP, 0x3); - break; - case V4L2_MBUS_FMT_RGB565_2X8_LE: - vs6624_write(sd, VS6624_IMG_FMT0, 0x4); - vs6624_write(sd, VS6624_RGB_SETUP, 0x0); - break; - default: - return -EINVAL; - } - - /* set image size */ - if ((fmt->width == VGA_WIDTH) && (fmt->height == VGA_HEIGHT)) - vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x2); - else if ((fmt->width == QVGA_WIDTH) && (fmt->height == QVGA_HEIGHT)) - vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x4); - else if ((fmt->width == QQVGA_WIDTH) && (fmt->height == QQVGA_HEIGHT)) - vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x6); - else if ((fmt->width == CIF_WIDTH) && (fmt->height == CIF_HEIGHT)) - vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x3); - else if ((fmt->width == QCIF_WIDTH) && (fmt->height == QCIF_HEIGHT)) - vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x5); - else if ((fmt->width == QQCIF_WIDTH) && (fmt->height == QQCIF_HEIGHT)) - vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x7); - else { - vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x8); - vs6624_write(sd, VS6624_MAN_HSIZE0_MSB, fmt->width >> 8); - vs6624_write(sd, VS6624_MAN_HSIZE0_LSB, fmt->width & 0xFF); - vs6624_write(sd, VS6624_MAN_VSIZE0_MSB, fmt->height >> 8); - vs6624_write(sd, VS6624_MAN_VSIZE0_LSB, fmt->height & 0xFF); - vs6624_write(sd, VS6624_CROP_CTRL0, 0x1); - } - - sensor->fmt = *fmt; - - return 0; -} - -static int vs6624_g_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) -{ - struct vs6624 *sensor = to_vs6624(sd); - - *fmt = sensor->fmt; - return 0; -} - -static int vs6624_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) -{ - struct vs6624 *sensor = to_vs6624(sd); - struct v4l2_captureparm *cp = &parms->parm.capture; - - if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - memset(cp, 0, sizeof(*cp)); - cp->capability = V4L2_CAP_TIMEPERFRAME; - cp->timeperframe.numerator = sensor->frame_rate.denominator; - cp->timeperframe.denominator = sensor->frame_rate.numerator; - return 0; -} - -static int vs6624_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) -{ - struct vs6624 *sensor = to_vs6624(sd); - struct v4l2_captureparm *cp = &parms->parm.capture; - struct v4l2_fract *tpf = &cp->timeperframe; - - if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (cp->extendedmode != 0) - return -EINVAL; - - if (tpf->numerator == 0 || tpf->denominator == 0 - || (tpf->denominator > tpf->numerator * MAX_FRAME_RATE)) { - /* reset to max frame rate */ - tpf->numerator = 1; - tpf->denominator = MAX_FRAME_RATE; - } - sensor->frame_rate.numerator = tpf->denominator; - sensor->frame_rate.denominator = tpf->numerator; - vs6624_write(sd, VS6624_DISABLE_FR_DAMPER, 0x0); - vs6624_write(sd, VS6624_FR_NUM_MSB, - sensor->frame_rate.numerator >> 8); - vs6624_write(sd, VS6624_FR_NUM_LSB, - sensor->frame_rate.numerator & 0xFF); - vs6624_write(sd, VS6624_FR_DEN, - sensor->frame_rate.denominator & 0xFF); - return 0; -} - -static int vs6624_s_stream(struct v4l2_subdev *sd, int enable) -{ - if (enable) - vs6624_write(sd, VS6624_USER_CMD, 0x2); - else - vs6624_write(sd, VS6624_USER_CMD, 0x4); - udelay(100); - return 0; -} - -static int vs6624_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *chip) -{ - int rev; - struct i2c_client *client = v4l2_get_subdevdata(sd); - - rev = (vs6624_read(sd, VS6624_FW_VSN_MAJOR) << 8) - | vs6624_read(sd, VS6624_FW_VSN_MINOR); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VS6624, rev); -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int vs6624_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - reg->val = vs6624_read(sd, reg->reg & 0xffff); - reg->size = 1; - return 0; -} - -static int vs6624_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - vs6624_write(sd, reg->reg & 0xffff, reg->val & 0xff); - return 0; -} -#endif - -static const struct v4l2_ctrl_ops vs6624_ctrl_ops = { - .s_ctrl = vs6624_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops vs6624_core_ops = { - .g_chip_ident = vs6624_g_chip_ident, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = vs6624_g_register, - .s_register = vs6624_s_register, -#endif -}; - -static const struct v4l2_subdev_video_ops vs6624_video_ops = { - .enum_mbus_fmt = vs6624_enum_mbus_fmt, - .try_mbus_fmt = vs6624_try_mbus_fmt, - .s_mbus_fmt = vs6624_s_mbus_fmt, - .g_mbus_fmt = vs6624_g_mbus_fmt, - .s_parm = vs6624_s_parm, - .g_parm = vs6624_g_parm, - .s_stream = vs6624_s_stream, -}; - -static const struct v4l2_subdev_ops vs6624_ops = { - .core = &vs6624_core_ops, - .video = &vs6624_video_ops, -}; - -static int __devinit vs6624_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct vs6624 *sensor; - struct v4l2_subdev *sd; - struct v4l2_ctrl_handler *hdl; - const unsigned *ce; - int ret; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) - return -EIO; - - ce = client->dev.platform_data; - if (ce == NULL) - return -EINVAL; - - ret = gpio_request(*ce, "VS6624 Chip Enable"); - if (ret) { - v4l_err(client, "failed to request GPIO %d\n", *ce); - return ret; - } - gpio_direction_output(*ce, 1); - /* wait 100ms before any further i2c writes are performed */ - mdelay(100); - - sensor = kzalloc(sizeof(*sensor), GFP_KERNEL); - if (sensor == NULL) { - gpio_free(*ce); - return -ENOMEM; - } - - sd = &sensor->sd; - v4l2_i2c_subdev_init(sd, client, &vs6624_ops); - - vs6624_writeregs(sd, vs6624_p1); - vs6624_write(sd, VS6624_MICRO_EN, 0x2); - vs6624_write(sd, VS6624_DIO_EN, 0x1); - mdelay(10); - vs6624_writeregs(sd, vs6624_p2); - - vs6624_writeregs(sd, vs6624_default); - vs6624_write(sd, VS6624_HSYNC_SETUP, 0xF); - vs6624_writeregs(sd, vs6624_run_setup); - - /* set frame rate */ - sensor->frame_rate.numerator = MAX_FRAME_RATE; - sensor->frame_rate.denominator = 1; - vs6624_write(sd, VS6624_DISABLE_FR_DAMPER, 0x0); - vs6624_write(sd, VS6624_FR_NUM_MSB, - sensor->frame_rate.numerator >> 8); - vs6624_write(sd, VS6624_FR_NUM_LSB, - sensor->frame_rate.numerator & 0xFF); - vs6624_write(sd, VS6624_FR_DEN, - sensor->frame_rate.denominator & 0xFF); - - sensor->fmt = vs6624_default_fmt; - sensor->ce_pin = *ce; - - v4l_info(client, "chip found @ 0x%02x (%s)\n", - client->addr << 1, client->adapter->name); - - hdl = &sensor->hdl; - v4l2_ctrl_handler_init(hdl, 4); - v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, - V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x87); - v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, - V4L2_CID_SATURATION, 0, 0xFF, 1, 0x78); - v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - /* hook the control handler into the driver */ - sd->ctrl_handler = hdl; - if (hdl->error) { - int err = hdl->error; - - v4l2_ctrl_handler_free(hdl); - kfree(sensor); - gpio_free(*ce); - return err; - } - - /* initialize the hardware to the default control values */ - ret = v4l2_ctrl_handler_setup(hdl); - if (ret) { - v4l2_ctrl_handler_free(hdl); - kfree(sensor); - gpio_free(*ce); - } - return ret; -} - -static int __devexit vs6624_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct vs6624 *sensor = to_vs6624(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(sd->ctrl_handler); - gpio_free(sensor->ce_pin); - kfree(sensor); - return 0; -} - -static const struct i2c_device_id vs6624_id[] = { - {"vs6624", 0}, - {}, -}; - -MODULE_DEVICE_TABLE(i2c, vs6624_id); - -static struct i2c_driver vs6624_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "vs6624", - }, - .probe = vs6624_probe, - .remove = __devexit_p(vs6624_remove), - .id_table = vs6624_id, -}; - -static __init int vs6624_init(void) -{ - return i2c_add_driver(&vs6624_driver); -} - -static __exit void vs6624_exit(void) -{ - i2c_del_driver(&vs6624_driver); -} - -module_init(vs6624_init); -module_exit(vs6624_exit); - -MODULE_DESCRIPTION("VS6624 sensor driver"); -MODULE_AUTHOR("Scott Jiang "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/vs6624_regs.h b/drivers/media/video/vs6624_regs.h deleted file mode 100644 index 6ba2ee25827..00000000000 --- a/drivers/media/video/vs6624_regs.h +++ /dev/null @@ -1,337 +0,0 @@ -/* - * vs6624 - ST VS6624 CMOS image sensor registers - * - * Copyright (c) 2011 Analog Devices Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * 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. - */ - -#ifndef _VS6624_REGS_H_ -#define _VS6624_REGS_H_ - -/* low level control registers */ -#define VS6624_MICRO_EN 0xC003 /* power enable for all MCU clock */ -#define VS6624_DIO_EN 0xC044 /* enable digital I/O */ -/* device parameters */ -#define VS6624_DEV_ID_MSB 0x0001 /* device id MSB */ -#define VS6624_DEV_ID_LSB 0x0002 /* device id LSB */ -#define VS6624_FW_VSN_MAJOR 0x0004 /* firmware version major */ -#define VS6624_FW_VSN_MINOR 0x0006 /* firmware version minor */ -#define VS6624_PATCH_VSN_MAJOR 0x0008 /* patch version major */ -#define VS6624_PATCH_VSN_MINOR 0x000A /* patch version minor */ -/* host interface manager control */ -#define VS6624_USER_CMD 0x0180 /* user level control of operating states */ -/* host interface manager status */ -#define VS6624_STATE 0x0202 /* current state of the mode manager */ -/* run mode control */ -#define VS6624_METER_ON 0x0280 /* if false AE and AWB are disabled */ -/* mode setup */ -#define VS6624_ACTIVE_PIPE_SETUP 0x0302 /* select the active bank for non view live mode */ -#define VS6624_SENSOR_MODE 0x0308 /* select the different sensor mode */ -/* pipe setup bank0 */ -#define VS6624_IMAGE_SIZE0 0x0380 /* required output dimension */ -#define VS6624_MAN_HSIZE0_MSB 0x0383 /* input required manual H size MSB */ -#define VS6624_MAN_HSIZE0_LSB 0x0384 /* input required manual H size LSB */ -#define VS6624_MAN_VSIZE0_MSB 0x0387 /* input required manual V size MSB */ -#define VS6624_MAN_VSIZE0_LSB 0x0388 /* input required manual V size LSB */ -#define VS6624_ZOOM_HSTEP0_MSB 0x038B /* set the zoom H step MSB */ -#define VS6624_ZOOM_HSTEP0_LSB 0x038C /* set the zoom H step LSB */ -#define VS6624_ZOOM_VSTEP0_MSB 0x038F /* set the zoom V step MSB */ -#define VS6624_ZOOM_VSTEP0_LSB 0x0390 /* set the zoom V step LSB */ -#define VS6624_ZOOM_CTRL0 0x0392 /* control zoon in, out and stop */ -#define VS6624_PAN_HSTEP0_MSB 0x0395 /* set the pan H step MSB */ -#define VS6624_PAN_HSTEP0_LSB 0x0396 /* set the pan H step LSB */ -#define VS6624_PAN_VSTEP0_MSB 0x0399 /* set the pan V step MSB */ -#define VS6624_PAN_VSTEP0_LSB 0x039A /* set the pan V step LSB */ -#define VS6624_PAN_CTRL0 0x039C /* control pan operation */ -#define VS6624_CROP_CTRL0 0x039E /* select cropping mode */ -#define VS6624_CROP_HSTART0_MSB 0x03A1 /* set the cropping H start address MSB */ -#define VS6624_CROP_HSTART0_LSB 0x03A2 /* set the cropping H start address LSB */ -#define VS6624_CROP_HSIZE0_MSB 0x03A5 /* set the cropping H size MSB */ -#define VS6624_CROP_HSIZE0_LSB 0x03A6 /* set the cropping H size LSB */ -#define VS6624_CROP_VSTART0_MSB 0x03A9 /* set the cropping V start address MSB */ -#define VS6624_CROP_VSTART0_LSB 0x03AA /* set the cropping V start address LSB */ -#define VS6624_CROP_VSIZE0_MSB 0x03AD /* set the cropping V size MSB */ -#define VS6624_CROP_VSIZE0_LSB 0x03AE /* set the cropping V size LSB */ -#define VS6624_IMG_FMT0 0x03B0 /* select required output image format */ -#define VS6624_BAYER_OUT_ALIGN0 0x03B2 /* set bayer output alignment */ -#define VS6624_CONTRAST0 0x03B4 /* contrast control for output */ -#define VS6624_SATURATION0 0x03B6 /* saturation control for output */ -#define VS6624_GAMMA0 0x03B8 /* gamma settings */ -#define VS6624_HMIRROR0 0x03BA /* horizontal image orientation flip */ -#define VS6624_VFLIP0 0x03BC /* vertical image orientation flip */ -#define VS6624_CHANNEL_ID0 0x03BE /* logical DMA channel number */ -/* pipe setup bank1 */ -#define VS6624_IMAGE_SIZE1 0x0400 /* required output dimension */ -#define VS6624_MAN_HSIZE1_MSB 0x0403 /* input required manual H size MSB */ -#define VS6624_MAN_HSIZE1_LSB 0x0404 /* input required manual H size LSB */ -#define VS6624_MAN_VSIZE1_MSB 0x0407 /* input required manual V size MSB */ -#define VS6624_MAN_VSIZE1_LSB 0x0408 /* input required manual V size LSB */ -#define VS6624_ZOOM_HSTEP1_MSB 0x040B /* set the zoom H step MSB */ -#define VS6624_ZOOM_HSTEP1_LSB 0x040C /* set the zoom H step LSB */ -#define VS6624_ZOOM_VSTEP1_MSB 0x040F /* set the zoom V step MSB */ -#define VS6624_ZOOM_VSTEP1_LSB 0x0410 /* set the zoom V step LSB */ -#define VS6624_ZOOM_CTRL1 0x0412 /* control zoon in, out and stop */ -#define VS6624_PAN_HSTEP1_MSB 0x0415 /* set the pan H step MSB */ -#define VS6624_PAN_HSTEP1_LSB 0x0416 /* set the pan H step LSB */ -#define VS6624_PAN_VSTEP1_MSB 0x0419 /* set the pan V step MSB */ -#define VS6624_PAN_VSTEP1_LSB 0x041A /* set the pan V step LSB */ -#define VS6624_PAN_CTRL1 0x041C /* control pan operation */ -#define VS6624_CROP_CTRL1 0x041E /* select cropping mode */ -#define VS6624_CROP_HSTART1_MSB 0x0421 /* set the cropping H start address MSB */ -#define VS6624_CROP_HSTART1_LSB 0x0422 /* set the cropping H start address LSB */ -#define VS6624_CROP_HSIZE1_MSB 0x0425 /* set the cropping H size MSB */ -#define VS6624_CROP_HSIZE1_LSB 0x0426 /* set the cropping H size LSB */ -#define VS6624_CROP_VSTART1_MSB 0x0429 /* set the cropping V start address MSB */ -#define VS6624_CROP_VSTART1_LSB 0x042A /* set the cropping V start address LSB */ -#define VS6624_CROP_VSIZE1_MSB 0x042D /* set the cropping V size MSB */ -#define VS6624_CROP_VSIZE1_LSB 0x042E /* set the cropping V size LSB */ -#define VS6624_IMG_FMT1 0x0430 /* select required output image format */ -#define VS6624_BAYER_OUT_ALIGN1 0x0432 /* set bayer output alignment */ -#define VS6624_CONTRAST1 0x0434 /* contrast control for output */ -#define VS6624_SATURATION1 0x0436 /* saturation control for output */ -#define VS6624_GAMMA1 0x0438 /* gamma settings */ -#define VS6624_HMIRROR1 0x043A /* horizontal image orientation flip */ -#define VS6624_VFLIP1 0x043C /* vertical image orientation flip */ -#define VS6624_CHANNEL_ID1 0x043E /* logical DMA channel number */ -/* view live control */ -#define VS6624_VIEW_LIVE_EN 0x0480 /* enable view live mode */ -#define VS6624_INIT_PIPE_SETUP 0x0482 /* select initial pipe setup bank */ -/* view live status */ -#define VS6624_CUR_PIPE_SETUP 0x0500 /* indicates most recently applied setup bank */ -/* power management */ -#define VS6624_TIME_TO_POWER_DOWN 0x0580 /* automatically transition time to stop mode */ -/* video timing parameter host inputs */ -#define VS6624_EXT_CLK_FREQ_NUM_MSB 0x0605 /* external clock frequency numerator MSB */ -#define VS6624_EXT_CLK_FREQ_NUM_LSB 0x0606 /* external clock frequency numerator LSB */ -#define VS6624_EXT_CLK_FREQ_DEN 0x0608 /* external clock frequency denominator */ -/* video timing control */ -#define VS6624_SYS_CLK_MODE 0x0880 /* decides system clock frequency */ -/* frame dimension parameter host inputs */ -#define VS6624_LIGHT_FREQ 0x0C80 /* AC frequency used for flicker free time */ -#define VS6624_FLICKER_COMPAT 0x0C82 /* flicker compatible frame length */ -/* static frame rate control */ -#define VS6624_FR_NUM_MSB 0x0D81 /* desired frame rate numerator MSB */ -#define VS6624_FR_NUM_LSB 0x0D82 /* desired frame rate numerator LSB */ -#define VS6624_FR_DEN 0x0D84 /* desired frame rate denominator */ -/* automatic frame rate control */ -#define VS6624_DISABLE_FR_DAMPER 0x0E80 /* defines frame rate mode */ -#define VS6624_MIN_DAMPER_OUT_MSB 0x0E8C /* minimum frame rate MSB */ -#define VS6624_MIN_DAMPER_OUT_LSB 0x0E8A /* minimum frame rate LSB */ -/* exposure controls */ -#define VS6624_EXPO_MODE 0x1180 /* exposure mode */ -#define VS6624_EXPO_METER 0x1182 /* weights to be associated with the zones */ -#define VS6624_EXPO_TIME_NUM 0x1184 /* exposure time numerator */ -#define VS6624_EXPO_TIME_DEN 0x1186 /* exposure time denominator */ -#define VS6624_EXPO_TIME_MSB 0x1189 /* exposure time for the Manual Mode MSB */ -#define VS6624_EXPO_TIME_LSB 0x118A /* exposure time for the Manual Mode LSB */ -#define VS6624_EXPO_COMPENSATION 0x1190 /* exposure compensation */ -#define VS6624_DIRECT_COARSE_MSB 0x1195 /* coarse integration lines for Direct Mode MSB */ -#define VS6624_DIRECT_COARSE_LSB 0x1196 /* coarse integration lines for Direct Mode LSB */ -#define VS6624_DIRECT_FINE_MSB 0x1199 /* fine integration pixels for Direct Mode MSB */ -#define VS6624_DIRECT_FINE_LSB 0x119A /* fine integration pixels for Direct Mode LSB */ -#define VS6624_DIRECT_ANAL_GAIN_MSB 0x119D /* analog gain for Direct Mode MSB */ -#define VS6624_DIRECT_ANAL_GAIN_LSB 0x119E /* analog gain for Direct Mode LSB */ -#define VS6624_DIRECT_DIGI_GAIN_MSB 0x11A1 /* digital gain for Direct Mode MSB */ -#define VS6624_DIRECT_DIGI_GAIN_LSB 0x11A2 /* digital gain for Direct Mode LSB */ -#define VS6624_FLASH_COARSE_MSB 0x11A5 /* coarse integration lines for Flash Gun Mode MSB */ -#define VS6624_FLASH_COARSE_LSB 0x11A6 /* coarse integration lines for Flash Gun Mode LSB */ -#define VS6624_FLASH_FINE_MSB 0x11A9 /* fine integration pixels for Flash Gun Mode MSB */ -#define VS6624_FLASH_FINE_LSB 0x11AA /* fine integration pixels for Flash Gun Mode LSB */ -#define VS6624_FLASH_ANAL_GAIN_MSB 0x11AD /* analog gain for Flash Gun Mode MSB */ -#define VS6624_FLASH_ANAL_GAIN_LSB 0x11AE /* analog gain for Flash Gun Mode LSB */ -#define VS6624_FLASH_DIGI_GAIN_MSB 0x11B1 /* digital gain for Flash Gun Mode MSB */ -#define VS6624_FLASH_DIGI_GAIN_LSB 0x11B2 /* digital gain for Flash Gun Mode LSB */ -#define VS6624_FREEZE_AE 0x11B4 /* freeze auto exposure */ -#define VS6624_MAX_INT_TIME_MSB 0x11B7 /* user maximum integration time MSB */ -#define VS6624_MAX_INT_TIME_LSB 0x11B8 /* user maximum integration time LSB */ -#define VS6624_FLASH_AG_THR_MSB 0x11BB /* recommend flash gun analog gain threshold MSB */ -#define VS6624_FLASH_AG_THR_LSB 0x11BC /* recommend flash gun analog gain threshold LSB */ -#define VS6624_ANTI_FLICKER_MODE 0x11C0 /* anti flicker mode */ -/* white balance control */ -#define VS6624_WB_MODE 0x1480 /* set white balance mode */ -#define VS6624_MAN_RG 0x1482 /* user setting for red channel gain */ -#define VS6624_MAN_GG 0x1484 /* user setting for green channel gain */ -#define VS6624_MAN_BG 0x1486 /* user setting for blue channel gain */ -#define VS6624_FLASH_RG_MSB 0x148B /* red gain for Flash Gun MSB */ -#define VS6624_FLASH_RG_LSB 0x148C /* red gain for Flash Gun LSB */ -#define VS6624_FLASH_GG_MSB 0x148F /* green gain for Flash Gun MSB */ -#define VS6624_FLASH_GG_LSB 0x1490 /* green gain for Flash Gun LSB */ -#define VS6624_FLASH_BG_MSB 0x1493 /* blue gain for Flash Gun MSB */ -#define VS6624_FLASH_BG_LSB 0x1494 /* blue gain for Flash Gun LSB */ -/* sensor setup */ -#define VS6624_BC_OFFSET 0x1990 /* Black Correction Offset */ -/* image stability */ -#define VS6624_STABLE_WB 0x1900 /* white balance stable */ -#define VS6624_STABLE_EXPO 0x1902 /* exposure stable */ -#define VS6624_STABLE 0x1906 /* system stable */ -/* flash control */ -#define VS6624_FLASH_MODE 0x1A80 /* flash mode */ -#define VS6624_FLASH_OFF_LINE_MSB 0x1A83 /* off line at flash pulse mode MSB */ -#define VS6624_FLASH_OFF_LINE_LSB 0x1A84 /* off line at flash pulse mode LSB */ -/* flash status */ -#define VS6624_FLASH_RECOM 0x1B00 /* flash gun is recommended */ -#define VS6624_FLASH_GRAB_COMPLETE 0x1B02 /* flash gun image has been grabbed */ -/* scythe filter controls */ -#define VS6624_SCYTHE_FILTER 0x1D80 /* disable scythe defect correction */ -/* jack filter controls */ -#define VS6624_JACK_FILTER 0x1E00 /* disable jack defect correction */ -/* demosaic control */ -#define VS6624_ANTI_ALIAS_FILTER 0x1E80 /* anti alias filter suppress */ -/* color matrix dampers */ -#define VS6624_CM_DISABLE 0x1F00 /* disable color matrix damper */ -#define VS6624_CM_LOW_THR_MSB 0x1F03 /* low threshold for exposure MSB */ -#define VS6624_CM_LOW_THR_LSB 0x1F04 /* low threshold for exposure LSB */ -#define VS6624_CM_HIGH_THR_MSB 0x1F07 /* high threshold for exposure MSB */ -#define VS6624_CM_HIGH_THR_LSB 0x1F08 /* high threshold for exposure LSB */ -#define VS6624_CM_MIN_OUT_MSB 0x1F0B /* minimum possible damper output MSB */ -#define VS6624_CM_MIN_OUT_LSB 0x1F0C /* minimum possible damper output LSB */ -/* peaking control */ -#define VS6624_PEAK_GAIN 0x2000 /* controls peaking gain */ -#define VS6624_PEAK_G_DISABLE 0x2002 /* disable peak gain damping */ -#define VS6624_PEAK_LOW_THR_G_MSB 0x2005 /* low threshold for exposure for gain MSB */ -#define VS6624_PEAK_LOW_THR_G_LSB 0x2006 /* low threshold for exposure for gain LSB */ -#define VS6624_PEAK_HIGH_THR_G_MSB 0x2009 /* high threshold for exposure for gain MSB */ -#define VS6624_PEAK_HIGH_THR_G_LSB 0x200A /* high threshold for exposure for gain LSB */ -#define VS6624_PEAK_MIN_OUT_G_MSB 0x200D /* minimum damper output for gain MSB */ -#define VS6624_PEAK_MIN_OUT_G_LSB 0x200E /* minimum damper output for gain LSB */ -#define VS6624_PEAK_LOW_THR 0x2010 /* adjust degree of coring */ -#define VS6624_PEAK_C_DISABLE 0x2012 /* disable coring damping */ -#define VS6624_PEAK_HIGH_THR 0x2014 /* adjust maximum gain */ -#define VS6624_PEAK_LOW_THR_C_MSB 0x2017 /* low threshold for exposure for coring MSB */ -#define VS6624_PEAK_LOW_THR_C_LSB 0x2018 /* low threshold for exposure for coring LSB */ -#define VS6624_PEAK_HIGH_THR_C_MSB 0x201B /* high threshold for exposure for coring MSB */ -#define VS6624_PEAK_HIGH_THR_C_LSB 0x201C /* high threshold for exposure for coring LSB */ -#define VS6624_PEAK_MIN_OUT_C_MSB 0x201F /* minimum damper output for coring MSB */ -#define VS6624_PEAK_MIN_OUT_C_LSB 0x2020 /* minimum damper output for coring LSB */ -/* pipe 0 RGB to YUV matrix manual control */ -#define VS6624_RYM0_MAN_CTRL 0x2180 /* enable manual RGB to YUV matrix */ -#define VS6624_RYM0_W00_MSB 0x2183 /* row 0 column 0 of YUV matrix MSB */ -#define VS6624_RYM0_W00_LSB 0x2184 /* row 0 column 0 of YUV matrix LSB */ -#define VS6624_RYM0_W01_MSB 0x2187 /* row 0 column 1 of YUV matrix MSB */ -#define VS6624_RYM0_W01_LSB 0x2188 /* row 0 column 1 of YUV matrix LSB */ -#define VS6624_RYM0_W02_MSB 0x218C /* row 0 column 2 of YUV matrix MSB */ -#define VS6624_RYM0_W02_LSB 0x218D /* row 0 column 2 of YUV matrix LSB */ -#define VS6624_RYM0_W10_MSB 0x2190 /* row 1 column 0 of YUV matrix MSB */ -#define VS6624_RYM0_W10_LSB 0x218F /* row 1 column 0 of YUV matrix LSB */ -#define VS6624_RYM0_W11_MSB 0x2193 /* row 1 column 1 of YUV matrix MSB */ -#define VS6624_RYM0_W11_LSB 0x2194 /* row 1 column 1 of YUV matrix LSB */ -#define VS6624_RYM0_W12_MSB 0x2197 /* row 1 column 2 of YUV matrix MSB */ -#define VS6624_RYM0_W12_LSB 0x2198 /* row 1 column 2 of YUV matrix LSB */ -#define VS6624_RYM0_W20_MSB 0x219B /* row 2 column 0 of YUV matrix MSB */ -#define VS6624_RYM0_W20_LSB 0x219C /* row 2 column 0 of YUV matrix LSB */ -#define VS6624_RYM0_W21_MSB 0x21A0 /* row 2 column 1 of YUV matrix MSB */ -#define VS6624_RYM0_W21_LSB 0x219F /* row 2 column 1 of YUV matrix LSB */ -#define VS6624_RYM0_W22_MSB 0x21A3 /* row 2 column 2 of YUV matrix MSB */ -#define VS6624_RYM0_W22_LSB 0x21A4 /* row 2 column 2 of YUV matrix LSB */ -#define VS6624_RYM0_YINY_MSB 0x21A7 /* Y in Y MSB */ -#define VS6624_RYM0_YINY_LSB 0x21A8 /* Y in Y LSB */ -#define VS6624_RYM0_YINCB_MSB 0x21AB /* Y in Cb MSB */ -#define VS6624_RYM0_YINCB_LSB 0x21AC /* Y in Cb LSB */ -#define VS6624_RYM0_YINCR_MSB 0x21B0 /* Y in Cr MSB */ -#define VS6624_RYM0_YINCR_LSB 0x21AF /* Y in Cr LSB */ -/* pipe 1 RGB to YUV matrix manual control */ -#define VS6624_RYM1_MAN_CTRL 0x2200 /* enable manual RGB to YUV matrix */ -#define VS6624_RYM1_W00_MSB 0x2203 /* row 0 column 0 of YUV matrix MSB */ -#define VS6624_RYM1_W00_LSB 0x2204 /* row 0 column 0 of YUV matrix LSB */ -#define VS6624_RYM1_W01_MSB 0x2207 /* row 0 column 1 of YUV matrix MSB */ -#define VS6624_RYM1_W01_LSB 0x2208 /* row 0 column 1 of YUV matrix LSB */ -#define VS6624_RYM1_W02_MSB 0x220C /* row 0 column 2 of YUV matrix MSB */ -#define VS6624_RYM1_W02_LSB 0x220D /* row 0 column 2 of YUV matrix LSB */ -#define VS6624_RYM1_W10_MSB 0x2210 /* row 1 column 0 of YUV matrix MSB */ -#define VS6624_RYM1_W10_LSB 0x220F /* row 1 column 0 of YUV matrix LSB */ -#define VS6624_RYM1_W11_MSB 0x2213 /* row 1 column 1 of YUV matrix MSB */ -#define VS6624_RYM1_W11_LSB 0x2214 /* row 1 column 1 of YUV matrix LSB */ -#define VS6624_RYM1_W12_MSB 0x2217 /* row 1 column 2 of YUV matrix MSB */ -#define VS6624_RYM1_W12_LSB 0x2218 /* row 1 column 2 of YUV matrix LSB */ -#define VS6624_RYM1_W20_MSB 0x221B /* row 2 column 0 of YUV matrix MSB */ -#define VS6624_RYM1_W20_LSB 0x221C /* row 2 column 0 of YUV matrix LSB */ -#define VS6624_RYM1_W21_MSB 0x2220 /* row 2 column 1 of YUV matrix MSB */ -#define VS6624_RYM1_W21_LSB 0x221F /* row 2 column 1 of YUV matrix LSB */ -#define VS6624_RYM1_W22_MSB 0x2223 /* row 2 column 2 of YUV matrix MSB */ -#define VS6624_RYM1_W22_LSB 0x2224 /* row 2 column 2 of YUV matrix LSB */ -#define VS6624_RYM1_YINY_MSB 0x2227 /* Y in Y MSB */ -#define VS6624_RYM1_YINY_LSB 0x2228 /* Y in Y LSB */ -#define VS6624_RYM1_YINCB_MSB 0x222B /* Y in Cb MSB */ -#define VS6624_RYM1_YINCB_LSB 0x222C /* Y in Cb LSB */ -#define VS6624_RYM1_YINCR_MSB 0x2220 /* Y in Cr MSB */ -#define VS6624_RYM1_YINCR_LSB 0x222F /* Y in Cr LSB */ -/* pipe 0 gamma manual control */ -#define VS6624_GAMMA_MAN_CTRL0 0x2280 /* enable manual gamma setup */ -#define VS6624_GAMMA_PEAK_R0 0x2282 /* peaked red channel gamma value */ -#define VS6624_GAMMA_PEAK_G0 0x2284 /* peaked green channel gamma value */ -#define VS6624_GAMMA_PEAK_B0 0x2286 /* peaked blue channel gamma value */ -#define VS6624_GAMMA_UNPEAK_R0 0x2288 /* unpeaked red channel gamma value */ -#define VS6624_GAMMA_UNPEAK_G0 0x228A /* unpeaked green channel gamma value */ -#define VS6624_GAMMA_UNPEAK_B0 0x228C /* unpeaked blue channel gamma value */ -/* pipe 1 gamma manual control */ -#define VS6624_GAMMA_MAN_CTRL1 0x2300 /* enable manual gamma setup */ -#define VS6624_GAMMA_PEAK_R1 0x2302 /* peaked red channel gamma value */ -#define VS6624_GAMMA_PEAK_G1 0x2304 /* peaked green channel gamma value */ -#define VS6624_GAMMA_PEAK_B1 0x2306 /* peaked blue channel gamma value */ -#define VS6624_GAMMA_UNPEAK_R1 0x2308 /* unpeaked red channel gamma value */ -#define VS6624_GAMMA_UNPEAK_G1 0x230A /* unpeaked green channel gamma value */ -#define VS6624_GAMMA_UNPEAK_B1 0x230C /* unpeaked blue channel gamma value */ -/* fade to black */ -#define VS6624_F2B_DISABLE 0x2480 /* disable fade to black */ -#define VS6624_F2B_BLACK_VAL_MSB 0x2483 /* black value MSB */ -#define VS6624_F2B_BLACK_VAL_LSB 0x2484 /* black value LSB */ -#define VS6624_F2B_LOW_THR_MSB 0x2487 /* low threshold for exposure MSB */ -#define VS6624_F2B_LOW_THR_LSB 0x2488 /* low threshold for exposure LSB */ -#define VS6624_F2B_HIGH_THR_MSB 0x248B /* high threshold for exposure MSB */ -#define VS6624_F2B_HIGH_THR_LSB 0x248C /* high threshold for exposure LSB */ -#define VS6624_F2B_MIN_OUT_MSB 0x248F /* minimum damper output MSB */ -#define VS6624_F2B_MIN_OUT_LSB 0x2490 /* minimum damper output LSB */ -/* output formatter control */ -#define VS6624_CODE_CK_EN 0x2580 /* code check enable */ -#define VS6624_BLANK_FMT 0x2582 /* blank format */ -#define VS6624_SYNC_CODE_SETUP 0x2584 /* sync code setup */ -#define VS6624_HSYNC_SETUP 0x2586 /* H sync setup */ -#define VS6624_VSYNC_SETUP 0x2588 /* V sync setup */ -#define VS6624_PCLK_SETUP 0x258A /* PCLK setup */ -#define VS6624_PCLK_EN 0x258C /* PCLK enable */ -#define VS6624_OPF_SP_SETUP 0x258E /* output formatter sp setup */ -#define VS6624_BLANK_DATA_MSB 0x2590 /* blank data MSB */ -#define VS6624_BLANK_DATA_LSB 0x2592 /* blank data LSB */ -#define VS6624_RGB_SETUP 0x2594 /* RGB setup */ -#define VS6624_YUV_SETUP 0x2596 /* YUV setup */ -#define VS6624_VSYNC_RIS_COARSE_H 0x2598 /* V sync rising coarse high */ -#define VS6624_VSYNC_RIS_COARSE_L 0x259A /* V sync rising coarse low */ -#define VS6624_VSYNC_RIS_FINE_H 0x259C /* V sync rising fine high */ -#define VS6624_VSYNC_RIS_FINE_L 0x259E /* V sync rising fine low */ -#define VS6624_VSYNC_FALL_COARSE_H 0x25A0 /* V sync falling coarse high */ -#define VS6624_VSYNC_FALL_COARSE_L 0x25A2 /* V sync falling coarse low */ -#define VS6624_VSYNC_FALL_FINE_H 0x25A4 /* V sync falling fine high */ -#define VS6624_VSYNC_FALL_FINE_L 0x25A6 /* V sync falling fine low */ -#define VS6624_HSYNC_RIS_H 0x25A8 /* H sync rising high */ -#define VS6624_HSYNC_RIS_L 0x25AA /* H sync rising low */ -#define VS6624_HSYNC_FALL_H 0x25AC /* H sync falling high */ -#define VS6624_HSYNC_FALL_L 0x25AE /* H sync falling low */ -#define VS6624_OUT_IF 0x25B0 /* output interface */ -#define VS6624_CCP_EXT_DATA 0x25B2 /* CCP extra data */ -/* NoRA controls */ -#define VS6624_NORA_DISABLE 0x2600 /* NoRA control mode */ -#define VS6624_NORA_USAGE 0x2602 /* usage */ -#define VS6624_NORA_SPLIT_KN 0x2604 /* split kn */ -#define VS6624_NORA_SPLIT_NI 0x2606 /* split ni */ -#define VS6624_NORA_TIGHT_G 0x2608 /* tight green */ -#define VS6624_NORA_DISABLE_NP 0x260A /* disable noro promoting */ -#define VS6624_NORA_LOW_THR_MSB 0x260D /* low threshold for exposure MSB */ -#define VS6624_NORA_LOW_THR_LSB 0x260E /* low threshold for exposure LSB */ -#define VS6624_NORA_HIGH_THR_MSB 0x2611 /* high threshold for exposure MSB */ -#define VS6624_NORA_HIGH_THR_LSB 0x2612 /* high threshold for exposure LSB */ -#define VS6624_NORA_MIN_OUT_MSB 0x2615 /* minimum damper output MSB */ -#define VS6624_NORA_MIN_OUT_LSB 0x2616 /* minimum damper output LSB */ - -#endif diff --git a/drivers/media/video/wm8739.c b/drivers/media/video/wm8739.c deleted file mode 100644 index 3bb99e93feb..00000000000 --- a/drivers/media/video/wm8739.c +++ /dev/null @@ -1,294 +0,0 @@ -/* - * wm8739 - * - * Copyright (C) 2005 T. Adachi - * - * Copyright (C) 2005 Hans Verkuil - * - Cleanup - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("wm8739 driver"); -MODULE_AUTHOR("T. Adachi, Hans Verkuil"); -MODULE_LICENSE("GPL"); - -static int debug; - -module_param(debug, int, 0644); - -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - - -/* ------------------------------------------------------------------------ */ - -enum { - R0 = 0, R1, - R5 = 5, R6, R7, R8, R9, R15 = 15, - TOT_REGS -}; - -struct wm8739_state { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - struct { - /* audio cluster */ - struct v4l2_ctrl *volume; - struct v4l2_ctrl *mute; - struct v4l2_ctrl *balance; - }; - u32 clock_freq; -}; - -static inline struct wm8739_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct wm8739_state, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct wm8739_state, hdl)->sd; -} - -/* ------------------------------------------------------------------------ */ - -static int wm8739_write(struct v4l2_subdev *sd, int reg, u16 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int i; - - if (reg < 0 || reg >= TOT_REGS) { - v4l2_err(sd, "Invalid register R%d\n", reg); - return -1; - } - - v4l2_dbg(1, debug, sd, "write: %02x %02x\n", reg, val); - - for (i = 0; i < 3; i++) - if (i2c_smbus_write_byte_data(client, - (reg << 1) | (val >> 8), val & 0xff) == 0) - return 0; - v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg); - return -1; -} - -static int wm8739_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - struct wm8739_state *state = to_state(sd); - unsigned int work_l, work_r; - u8 vol_l; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ - u8 vol_r; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ - u16 mute; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - break; - - default: - return -EINVAL; - } - - /* normalize ( 65535 to 0 -> 31 to 0 (12dB to -34.5dB) ) */ - work_l = (min(65536 - state->balance->val, 32768) * state->volume->val) / 32768; - work_r = (min(state->balance->val, 32768) * state->volume->val) / 32768; - - vol_l = (long)work_l * 31 / 65535; - vol_r = (long)work_r * 31 / 65535; - - /* set audio volume etc. */ - mute = state->mute->val ? 0x80 : 0; - - /* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB - * Default setting: 0x17 = 0 dB - */ - wm8739_write(sd, R0, (vol_l & 0x1f) | mute); - wm8739_write(sd, R1, (vol_r & 0x1f) | mute); - return 0; -} - -/* ------------------------------------------------------------------------ */ - -static int wm8739_s_clock_freq(struct v4l2_subdev *sd, u32 audiofreq) -{ - struct wm8739_state *state = to_state(sd); - - state->clock_freq = audiofreq; - /* de-activate */ - wm8739_write(sd, R9, 0x000); - switch (audiofreq) { - case 44100: - /* 256fps, fs=44.1k */ - wm8739_write(sd, R8, 0x020); - break; - case 48000: - /* 256fps, fs=48k */ - wm8739_write(sd, R8, 0x000); - break; - case 32000: - /* 256fps, fs=32k */ - wm8739_write(sd, R8, 0x018); - break; - default: - break; - } - /* activate */ - wm8739_write(sd, R9, 0x001); - return 0; -} - -static int wm8739_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8739, 0); -} - -static int wm8739_log_status(struct v4l2_subdev *sd) -{ - struct wm8739_state *state = to_state(sd); - - v4l2_info(sd, "Frequency: %u Hz\n", state->clock_freq); - v4l2_ctrl_handler_log_status(&state->hdl, sd->name); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_ctrl_ops wm8739_ctrl_ops = { - .s_ctrl = wm8739_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops wm8739_core_ops = { - .log_status = wm8739_log_status, - .g_chip_ident = wm8739_g_chip_ident, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, -}; - -static const struct v4l2_subdev_audio_ops wm8739_audio_ops = { - .s_clock_freq = wm8739_s_clock_freq, -}; - -static const struct v4l2_subdev_ops wm8739_ops = { - .core = &wm8739_core_ops, - .audio = &wm8739_audio_ops, -}; - -/* ------------------------------------------------------------------------ */ - -/* i2c implementation */ - -static int wm8739_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct wm8739_state *state; - struct v4l2_subdev *sd; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - state = kzalloc(sizeof(struct wm8739_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &wm8739_ops); - v4l2_ctrl_handler_init(&state->hdl, 2); - state->volume = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops, - V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 50736); - state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops, - V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); - state->balance = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops, - V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768); - sd->ctrl_handler = &state->hdl; - if (state->hdl.error) { - int err = state->hdl.error; - - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return err; - } - v4l2_ctrl_cluster(3, &state->volume); - - state->clock_freq = 48000; - - /* Initialize wm8739 */ - - /* reset */ - wm8739_write(sd, R15, 0x00); - /* filter setting, high path, offet clear */ - wm8739_write(sd, R5, 0x000); - /* ADC, OSC, Power Off mode Disable */ - wm8739_write(sd, R6, 0x000); - /* Digital Audio interface format: - Enable Master mode, 24 bit, MSB first/left justified */ - wm8739_write(sd, R7, 0x049); - /* sampling control: normal, 256fs, 48KHz sampling rate */ - wm8739_write(sd, R8, 0x000); - /* activate */ - wm8739_write(sd, R9, 0x001); - /* set volume/mute */ - v4l2_ctrl_handler_setup(&state->hdl); - return 0; -} - -static int wm8739_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct wm8739_state *state = to_state(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&state->hdl); - kfree(to_state(sd)); - return 0; -} - -static const struct i2c_device_id wm8739_id[] = { - { "wm8739", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, wm8739_id); - -static struct i2c_driver wm8739_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "wm8739", - }, - .probe = wm8739_probe, - .remove = wm8739_remove, - .id_table = wm8739_id, -}; - -module_i2c_driver(wm8739_driver); diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c deleted file mode 100644 index bee77ea9f49..00000000000 --- a/drivers/media/video/wm8775.c +++ /dev/null @@ -1,342 +0,0 @@ -/* - * wm8775 - driver version 0.0.1 - * - * Copyright (C) 2004 Ulf Eklund - * - * Based on saa7115 driver - * - * Copyright (C) 2005 Hans Verkuil - * - Cleanup - * - V4L2 API update - * - sound fixes - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("wm8775 driver"); -MODULE_AUTHOR("Ulf Eklund, Hans Verkuil"); -MODULE_LICENSE("GPL"); - - - -/* ----------------------------------------------------------------------- */ - -enum { - R7 = 7, R11 = 11, - R12, R13, R14, R15, R16, R17, R18, R19, R20, R21, R23 = 23, - TOT_REGS -}; - -#define ALC_HOLD 0x85 /* R17: use zero cross detection, ALC hold time 42.6 ms */ -#define ALC_EN 0x100 /* R17: ALC enable */ - -struct wm8775_state { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - struct v4l2_ctrl *mute; - struct v4l2_ctrl *vol; - struct v4l2_ctrl *bal; - struct v4l2_ctrl *loud; - u8 input; /* Last selected input (0-0xf) */ -}; - -static inline struct wm8775_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct wm8775_state, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct wm8775_state, hdl)->sd; -} - -static int wm8775_write(struct v4l2_subdev *sd, int reg, u16 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int i; - - if (reg < 0 || reg >= TOT_REGS) { - v4l2_err(sd, "Invalid register R%d\n", reg); - return -1; - } - - for (i = 0; i < 3; i++) - if (i2c_smbus_write_byte_data(client, - (reg << 1) | (val >> 8), val & 0xff) == 0) - return 0; - v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg); - return -1; -} - -static void wm8775_set_audio(struct v4l2_subdev *sd, int quietly) -{ - struct wm8775_state *state = to_state(sd); - u8 vol_l, vol_r; - int muted = 0 != state->mute->val; - u16 volume = (u16)state->vol->val; - u16 balance = (u16)state->bal->val; - - /* normalize ( 65535 to 0 -> 255 to 0 (+24dB to -103dB) ) */ - vol_l = (min(65536 - balance, 32768) * volume) >> 23; - vol_r = (min(balance, (u16)32768) * volume) >> 23; - - /* Mute */ - if (muted || quietly) - wm8775_write(sd, R21, 0x0c0 | state->input); - - wm8775_write(sd, R14, vol_l | 0x100); /* 0x100= Left channel ADC zero cross enable */ - wm8775_write(sd, R15, vol_r | 0x100); /* 0x100= Right channel ADC zero cross enable */ - - /* Un-mute */ - if (!muted) - wm8775_write(sd, R21, state->input); -} - -static int wm8775_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct wm8775_state *state = to_state(sd); - - /* There are 4 inputs and one output. Zero or more inputs - are multiplexed together to the output. Hence there are - 16 combinations. - If only one input is active (the normal case) then the - input values 1, 2, 4 or 8 should be used. */ - if (input > 15) { - v4l2_err(sd, "Invalid input %d.\n", input); - return -EINVAL; - } - state->input = input; - if (!v4l2_ctrl_g_ctrl(state->mute)) - return 0; - if (!v4l2_ctrl_g_ctrl(state->vol)) - return 0; - if (!v4l2_ctrl_g_ctrl(state->bal)) - return 0; - wm8775_set_audio(sd, 1); - return 0; -} - -static int wm8775_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_BALANCE: - wm8775_set_audio(sd, 0); - return 0; - case V4L2_CID_AUDIO_LOUDNESS: - wm8775_write(sd, R17, (ctrl->val ? ALC_EN : 0) | ALC_HOLD); - return 0; - } - return -EINVAL; -} - -static int wm8775_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8775, 0); -} - -static int wm8775_log_status(struct v4l2_subdev *sd) -{ - struct wm8775_state *state = to_state(sd); - - v4l2_info(sd, "Input: %d\n", state->input); - v4l2_ctrl_handler_log_status(&state->hdl, sd->name); - return 0; -} - -static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) -{ - wm8775_set_audio(sd, 0); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_ctrl_ops wm8775_ctrl_ops = { - .s_ctrl = wm8775_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops wm8775_core_ops = { - .log_status = wm8775_log_status, - .g_chip_ident = wm8775_g_chip_ident, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, -}; - -static const struct v4l2_subdev_tuner_ops wm8775_tuner_ops = { - .s_frequency = wm8775_s_frequency, -}; - -static const struct v4l2_subdev_audio_ops wm8775_audio_ops = { - .s_routing = wm8775_s_routing, -}; - -static const struct v4l2_subdev_ops wm8775_ops = { - .core = &wm8775_core_ops, - .tuner = &wm8775_tuner_ops, - .audio = &wm8775_audio_ops, -}; - -/* ----------------------------------------------------------------------- */ - -/* i2c implementation */ - -/* - * Generic i2c probe - * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' - */ - -static int wm8775_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct wm8775_state *state; - struct v4l2_subdev *sd; - int err; - bool is_nova_s = false; - - if (client->dev.platform_data) { - struct wm8775_platform_data *data = client->dev.platform_data; - is_nova_s = data->is_nova_s; - } - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - v4l_info(client, "chip found @ 0x%02x (%s)\n", - client->addr << 1, client->adapter->name); - - state = kzalloc(sizeof(struct wm8775_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &wm8775_ops); - state->input = 2; - - v4l2_ctrl_handler_init(&state->hdl, 4); - state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, - V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); - state->vol = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, - V4L2_CID_AUDIO_VOLUME, 0, 65535, (65535+99)/100, 0xCF00); /* 0dB*/ - state->bal = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, - V4L2_CID_AUDIO_BALANCE, 0, 65535, (65535+99)/100, 32768); - state->loud = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, - V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 1); - sd->ctrl_handler = &state->hdl; - err = state->hdl.error; - if (err) { - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return err; - } - - /* Initialize wm8775 */ - - /* RESET */ - wm8775_write(sd, R23, 0x000); - /* Disable zero cross detect timeout */ - wm8775_write(sd, R7, 0x000); - /* HPF enable, left justified, 24-bit (Philips) mode */ - wm8775_write(sd, R11, 0x021); - /* Master mode, clock ratio 256fs */ - wm8775_write(sd, R12, 0x102); - /* Powered up */ - wm8775_write(sd, R13, 0x000); - - if (!is_nova_s) { - /* ADC gain +2.5dB, enable zero cross */ - wm8775_write(sd, R14, 0x1d4); - /* ADC gain +2.5dB, enable zero cross */ - wm8775_write(sd, R15, 0x1d4); - /* ALC Stereo, ALC target level -1dB FS max gain +8dB */ - wm8775_write(sd, R16, 0x1bf); - /* Enable gain control, use zero cross detection, - ALC hold time 42.6 ms */ - wm8775_write(sd, R17, 0x185); - } else { - /* ALC stereo, ALC target level -5dB FS, ALC max gain +8dB */ - wm8775_write(sd, R16, 0x1bb); - /* Set ALC mode and hold time */ - wm8775_write(sd, R17, (state->loud->val ? ALC_EN : 0) | ALC_HOLD); - } - /* ALC gain ramp up delay 34 s, ALC gain ramp down delay 33 ms */ - wm8775_write(sd, R18, 0x0a2); - /* Enable noise gate, threshold -72dBfs */ - wm8775_write(sd, R19, 0x005); - if (!is_nova_s) { - /* Transient window 4ms, lower PGA gain limit -1dB */ - wm8775_write(sd, R20, 0x07a); - /* LRBOTH = 1, use input 2. */ - wm8775_write(sd, R21, 0x102); - } else { - /* Transient window 4ms, ALC min gain -5dB */ - wm8775_write(sd, R20, 0x0fb); - - wm8775_set_audio(sd, 1); /* set volume/mute/mux */ - } - return 0; -} - -static int wm8775_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct wm8775_state *state = to_state(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return 0; -} - -static const struct i2c_device_id wm8775_id[] = { - { "wm8775", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, wm8775_id); - -static struct i2c_driver wm8775_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "wm8775", - }, - .probe = wm8775_probe, - .remove = wm8775_remove, - .id_table = wm8775_id, -}; - -module_i2c_driver(wm8775_driver); -- cgit v1.2.3-70-g09d2 From 88f8472c9fc6c08f5113887471f1f4aabf7b2929 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 16 Aug 2012 16:57:21 -0300 Subject: [media] Fix some Makefile rules On a few places, := were using instead of +=, causing drivers to not compile. While here, standardize the usage of += on all cases where multiple lines are needed, and for obj-y/obj-m targets, and := when just one line is needed, on -obj rules. Reported-by: Hans Verkuil Identified-by: Antti Polosaari Tested-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/b2c2/Makefile | 5 ++-- drivers/media/dvb-frontends/Makefile | 8 +++--- drivers/media/firewire/Makefile | 2 +- drivers/media/mmc/Makefile | 2 +- drivers/media/pci/Makefile | 2 +- drivers/media/pci/cx25821/Makefile | 2 +- drivers/media/pci/saa7134/Makefile | 2 +- drivers/media/platform/omap/Makefile | 2 +- drivers/media/usb/Makefile | 4 +-- drivers/media/usb/b2c2/Makefile | 2 +- drivers/media/usb/dvb-usb-v2/Makefile | 29 ++++++++++--------- drivers/media/usb/dvb-usb/Makefile | 53 ++++++++++++++++++----------------- drivers/media/usb/em28xx/Makefile | 2 +- drivers/media/usb/pwc/Makefile | 2 +- drivers/media/usb/tm6000/Makefile | 2 +- 15 files changed, 61 insertions(+), 58 deletions(-) (limited to 'drivers/media/pci/saa7134') diff --git a/drivers/media/common/b2c2/Makefile b/drivers/media/common/b2c2/Makefile index 48a4c906a17..24993a5b38b 100644 --- a/drivers/media/common/b2c2/Makefile +++ b/drivers/media/common/b2c2/Makefile @@ -1,5 +1,6 @@ -b2c2-flexcop-objs = flexcop.o flexcop-fe-tuner.o flexcop-i2c.o \ - flexcop-sram.o flexcop-eeprom.o flexcop-misc.o flexcop-hw-filter.o +b2c2-flexcop-objs += flexcop.o flexcop-fe-tuner.o flexcop-i2c.o +b2c2-flexcop-objs += flexcop-sram.o flexcop-eeprom.o flexcop-misc.o +b2c2-flexcop-objs += flexcop-hw-filter.o obj-$(CONFIG_DVB_B2C2_FLEXCOP) += b2c2-flexcop.o ccflags-y += -Idrivers/media/dvb-core/ diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index 208bc496b90..7eb73bbd2e2 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -5,10 +5,10 @@ ccflags-y += -I$(srctree)/drivers/media/dvb-core/ ccflags-y += -I$(srctree)/drivers/media/tuners/ -stb0899-objs = stb0899_drv.o stb0899_algo.o -stv0900-objs = stv0900_core.o stv0900_sw.o -drxd-objs = drxd_firm.o drxd_hard.o -cxd2820r-objs = cxd2820r_core.o cxd2820r_c.o cxd2820r_t.o cxd2820r_t2.o +stb0899-objs := stb0899_drv.o stb0899_algo.o +stv0900-objs := stv0900_core.o stv0900_sw.o +drxd-objs := drxd_firm.o drxd_hard.o +cxd2820r-objs := cxd2820r_core.o cxd2820r_c.o cxd2820r_t.o cxd2820r_t2.o drxk-objs := drxk_hard.o obj-$(CONFIG_DVB_PLL) += dvb-pll.o diff --git a/drivers/media/firewire/Makefile b/drivers/media/firewire/Makefile index f3148138c96..239481344d7 100644 --- a/drivers/media/firewire/Makefile +++ b/drivers/media/firewire/Makefile @@ -1,6 +1,6 @@ obj-$(CONFIG_DVB_FIREDTV) += firedtv.o -firedtv-y := firedtv-avc.o firedtv-ci.o firedtv-dvb.o firedtv-fe.o firedtv-fw.o +firedtv-y += firedtv-avc.o firedtv-ci.o firedtv-dvb.o firedtv-fe.o firedtv-fw.o firedtv-$(CONFIG_DVB_FIREDTV_INPUT) += firedtv-rc.o ccflags-y += -Idrivers/media/dvb-core diff --git a/drivers/media/mmc/Makefile b/drivers/media/mmc/Makefile index dacd3cbb80d..31e297a202f 100644 --- a/drivers/media/mmc/Makefile +++ b/drivers/media/mmc/Makefile @@ -1 +1 @@ -obj-y := siano/ +obj-y += siano/ diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile index c8dc6c77530..35cc57862c0 100644 --- a/drivers/media/pci/Makefile +++ b/drivers/media/pci/Makefile @@ -2,7 +2,7 @@ # Makefile for the kernel multimedia device drivers. # -obj-y := ttpci/ \ +obj-y += ttpci/ \ b2c2/ \ pluto2/ \ dm1105/ \ diff --git a/drivers/media/pci/cx25821/Makefile b/drivers/media/pci/cx25821/Makefile index c038941d605..5bf3ea4c155 100644 --- a/drivers/media/pci/cx25821/Makefile +++ b/drivers/media/pci/cx25821/Makefile @@ -7,7 +7,7 @@ cx25821-y := cx25821-core.o cx25821-cards.o cx25821-i2c.o \ obj-$(CONFIG_VIDEO_CX25821) += cx25821.o obj-$(CONFIG_VIDEO_CX25821_ALSA) += cx25821-alsa.o -ccflags-y := -Idrivers/media/i2c +ccflags-y += -Idrivers/media/i2c ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/pci/saa7134/Makefile b/drivers/media/pci/saa7134/Makefile index 9e510c1459f..35375480ed4 100644 --- a/drivers/media/pci/saa7134/Makefile +++ b/drivers/media/pci/saa7134/Makefile @@ -1,5 +1,5 @@ -saa7134-y := saa7134-cards.o saa7134-core.o saa7134-i2c.o +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 diff --git a/drivers/media/platform/omap/Makefile b/drivers/media/platform/omap/Makefile index fc410b438f7..d80df41fde2 100644 --- a/drivers/media/platform/omap/Makefile +++ b/drivers/media/platform/omap/Makefile @@ -3,6 +3,6 @@ # # OMAP2/3 Display driver -omap-vout-y := omap_vout.o omap_voutlib.o +omap-vout-y += omap_vout.o omap_voutlib.o omap-vout-$(CONFIG_VIDEO_OMAP2_VOUT_VRFB) += omap_vout_vrfb.o obj-$(CONFIG_VIDEO_OMAP2_VOUT) += omap-vout.o diff --git a/drivers/media/usb/Makefile b/drivers/media/usb/Makefile index 63e37bb2ed7..7f51d7e5f73 100644 --- a/drivers/media/usb/Makefile +++ b/drivers/media/usb/Makefile @@ -3,8 +3,8 @@ # # DVB USB-only drivers -obj-y := ttusb-dec/ ttusb-budget/ dvb-usb/ dvb-usb-v2/ siano/ b2c2/ -obj-y := zr364xx/ stkwebcam/ s2255/ +obj-y += ttusb-dec/ ttusb-budget/ dvb-usb/ dvb-usb-v2/ siano/ b2c2/ +obj-y += zr364xx/ stkwebcam/ s2255/ obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ obj-$(CONFIG_USB_GSPCA) += gspca/ diff --git a/drivers/media/usb/b2c2/Makefile b/drivers/media/usb/b2c2/Makefile index ace9d76f0b3..2778c19a45e 100644 --- a/drivers/media/usb/b2c2/Makefile +++ b/drivers/media/usb/b2c2/Makefile @@ -1,4 +1,4 @@ -b2c2-flexcop-usb-objs = flexcop-usb.o +b2c2-flexcop-usb-objs := flexcop-usb.o obj-$(CONFIG_DVB_B2C2_FLEXCOP_USB) += b2c2-flexcop-usb.o ccflags-y += -Idrivers/media/dvb-core/ diff --git a/drivers/media/usb/dvb-usb-v2/Makefile b/drivers/media/usb/dvb-usb-v2/Makefile index 62568430418..58027fd43a0 100644 --- a/drivers/media/usb/dvb-usb-v2/Makefile +++ b/drivers/media/usb/dvb-usb-v2/Makefile @@ -1,45 +1,46 @@ -dvb_usbv2-objs = dvb_usb_core.o dvb_usb_urb.o usb_urb.o +dvb_usbv2-objs := dvb_usb_core.o dvb_usb_urb.o usb_urb.o obj-$(CONFIG_DVB_USB_V2) += dvb_usbv2.o -dvb_usb_cypress_firmware-objs = cypress_firmware.o +dvb_usb_cypress_firmware-objs := cypress_firmware.o obj-$(CONFIG_DVB_USB_CYPRESS_FIRMWARE) += dvb_usb_cypress_firmware.o -dvb-usb-af9015-objs = af9015.o +dvb-usb-af9015-objs := af9015.o obj-$(CONFIG_DVB_USB_AF9015) += dvb-usb-af9015.o -dvb-usb-af9035-objs = af9035.o +dvb-usb-af9035-objs := af9035.o obj-$(CONFIG_DVB_USB_AF9035) += dvb-usb-af9035.o -dvb-usb-anysee-objs = anysee.o +dvb-usb-anysee-objs := anysee.o obj-$(CONFIG_DVB_USB_ANYSEE) += dvb-usb-anysee.o -dvb-usb-au6610-objs = au6610.o +dvb-usb-au6610-objs := au6610.o obj-$(CONFIG_DVB_USB_AU6610) += dvb-usb-au6610.o -dvb-usb-az6007-objs = az6007.o +dvb-usb-az6007-objs := az6007.o obj-$(CONFIG_DVB_USB_AZ6007) += dvb-usb-az6007.o -dvb-usb-ce6230-objs = ce6230.o +dvb-usb-ce6230-objs := ce6230.o obj-$(CONFIG_DVB_USB_CE6230) += dvb-usb-ce6230.o -dvb-usb-ec168-objs = ec168.o +dvb-usb-ec168-objs := ec168.o obj-$(CONFIG_DVB_USB_EC168) += dvb-usb-ec168.o -dvb-usb-it913x-objs = it913x.o +dvb-usb-it913x-objs := it913x.o obj-$(CONFIG_DVB_USB_IT913X) += dvb-usb-it913x.o -dvb-usb-lmedm04-objs = lmedm04.o +dvb-usb-lmedm04-objs := lmedm04.o obj-$(CONFIG_DVB_USB_LME2510) += dvb-usb-lmedm04.o -dvb-usb-gl861-objs = gl861.o +dvb-usb-gl861-objs := gl861.o obj-$(CONFIG_DVB_USB_GL861) += dvb-usb-gl861.o -dvb-usb-mxl111sf-objs = mxl111sf.o mxl111sf-phy.o mxl111sf-i2c.o mxl111sf-gpio.o +dvb-usb-mxl111sf-objs += mxl111sf.o mxl111sf-phy.o mxl111sf-i2c.o +dvb-usb-mxl111sf-objs += mxl111sf-gpio.o obj-$(CONFIG_DVB_USB_MXL111SF) += dvb-usb-mxl111sf.o obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-demod.o obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-tuner.o -dvb-usb-rtl28xxu-objs = rtl28xxu.o +dvb-usb-rtl28xxu-objs := rtl28xxu.o obj-$(CONFIG_DVB_USB_RTL28XXU) += dvb-usb-rtl28xxu.o ccflags-y += -I$(srctree)/drivers/media/dvb-core diff --git a/drivers/media/usb/dvb-usb/Makefile b/drivers/media/usb/dvb-usb/Makefile index d3ab1e74647..acdd1efd4e7 100644 --- a/drivers/media/usb/dvb-usb/Makefile +++ b/drivers/media/usb/dvb-usb/Makefile @@ -1,78 +1,79 @@ -dvb-usb-objs = dvb-usb-firmware.o dvb-usb-init.o dvb-usb-urb.o dvb-usb-i2c.o dvb-usb-dvb.o dvb-usb-remote.o usb-urb.o +dvb-usb-objs += dvb-usb-firmware.o dvb-usb-init.o dvb-usb-urb.o dvb-usb-i2c.o +dvb-usb-objs += dvb-usb-dvb.o dvb-usb-remote.o usb-urb.o obj-$(CONFIG_DVB_USB) += dvb-usb.o -dvb-usb-vp7045-objs = vp7045.o vp7045-fe.o +dvb-usb-vp7045-objs := vp7045.o vp7045-fe.o obj-$(CONFIG_DVB_USB_VP7045) += dvb-usb-vp7045.o -dvb-usb-vp702x-objs = vp702x.o vp702x-fe.o +dvb-usb-vp702x-objs := vp702x.o vp702x-fe.o obj-$(CONFIG_DVB_USB_VP702X) += dvb-usb-vp702x.o -dvb-usb-gp8psk-objs = gp8psk.o gp8psk-fe.o +dvb-usb-gp8psk-objs := gp8psk.o gp8psk-fe.o obj-$(CONFIG_DVB_USB_GP8PSK) += dvb-usb-gp8psk.o -dvb-usb-dtt200u-objs = dtt200u.o dtt200u-fe.o +dvb-usb-dtt200u-objs := dtt200u.o dtt200u-fe.o obj-$(CONFIG_DVB_USB_DTT200U) += dvb-usb-dtt200u.o -dvb-usb-dibusb-common-objs = dibusb-common.o +dvb-usb-dibusb-common-objs := dibusb-common.o -dvb-usb-a800-objs = a800.o +dvb-usb-a800-objs := a800.o obj-$(CONFIG_DVB_USB_A800) += dvb-usb-dibusb-common.o dvb-usb-a800.o -dvb-usb-dibusb-mb-objs = dibusb-mb.o +dvb-usb-dibusb-mb-objs := dibusb-mb.o obj-$(CONFIG_DVB_USB_DIBUSB_MB) += dvb-usb-dibusb-common.o dvb-usb-dibusb-mb.o -dvb-usb-dibusb-mc-objs = dibusb-mc.o +dvb-usb-dibusb-mc-objs := dibusb-mc.o obj-$(CONFIG_DVB_USB_DIBUSB_MC) += dvb-usb-dibusb-common.o dvb-usb-dibusb-mc.o -dvb-usb-nova-t-usb2-objs = nova-t-usb2.o +dvb-usb-nova-t-usb2-objs := nova-t-usb2.o obj-$(CONFIG_DVB_USB_NOVA_T_USB2) += dvb-usb-dibusb-common.o dvb-usb-nova-t-usb2.o -dvb-usb-umt-010-objs = umt-010.o +dvb-usb-umt-010-objs := umt-010.o obj-$(CONFIG_DVB_USB_UMT_010) += dvb-usb-dibusb-common.o dvb-usb-umt-010.o -dvb-usb-m920x-objs = m920x.o +dvb-usb-m920x-objs := m920x.o obj-$(CONFIG_DVB_USB_M920X) += dvb-usb-m920x.o -dvb-usb-digitv-objs = digitv.o +dvb-usb-digitv-objs := digitv.o obj-$(CONFIG_DVB_USB_DIGITV) += dvb-usb-digitv.o -dvb-usb-cxusb-objs = cxusb.o +dvb-usb-cxusb-objs := cxusb.o obj-$(CONFIG_DVB_USB_CXUSB) += dvb-usb-cxusb.o -dvb-usb-ttusb2-objs = ttusb2.o +dvb-usb-ttusb2-objs := ttusb2.o obj-$(CONFIG_DVB_USB_TTUSB2) += dvb-usb-ttusb2.o -dvb-usb-dib0700-objs = dib0700_core.o dib0700_devices.o +dvb-usb-dib0700-objs := dib0700_core.o dib0700_devices.o obj-$(CONFIG_DVB_USB_DIB0700) += dvb-usb-dib0700.o -dvb-usb-opera-objs = opera1.o +dvb-usb-opera-objs := opera1.o obj-$(CONFIG_DVB_USB_OPERA1) += dvb-usb-opera.o -dvb-usb-af9005-objs = af9005.o af9005-fe.o +dvb-usb-af9005-objs := af9005.o af9005-fe.o obj-$(CONFIG_DVB_USB_AF9005) += dvb-usb-af9005.o -dvb-usb-af9005-remote-objs = af9005-remote.o +dvb-usb-af9005-remote-objs := af9005-remote.o obj-$(CONFIG_DVB_USB_AF9005_REMOTE) += dvb-usb-af9005-remote.o -dvb-usb-pctv452e-objs = pctv452e.o +dvb-usb-pctv452e-objs := pctv452e.o obj-$(CONFIG_DVB_USB_PCTV452E) += dvb-usb-pctv452e.o -dvb-usb-dw2102-objs = dw2102.o +dvb-usb-dw2102-objs := dw2102.o obj-$(CONFIG_DVB_USB_DW2102) += dvb-usb-dw2102.o -dvb-usb-dtv5100-objs = dtv5100.o +dvb-usb-dtv5100-objs := dtv5100.o obj-$(CONFIG_DVB_USB_DTV5100) += dvb-usb-dtv5100.o -dvb-usb-cinergyT2-objs = cinergyT2-core.o cinergyT2-fe.o +dvb-usb-cinergyT2-objs := cinergyT2-core.o cinergyT2-fe.o obj-$(CONFIG_DVB_USB_CINERGY_T2) += dvb-usb-cinergyT2.o -dvb-usb-friio-objs = friio.o friio-fe.o +dvb-usb-friio-objs := friio.o friio-fe.o obj-$(CONFIG_DVB_USB_FRIIO) += dvb-usb-friio.o -dvb-usb-az6027-objs = az6027.o +dvb-usb-az6027-objs := az6027.o obj-$(CONFIG_DVB_USB_AZ6027) += dvb-usb-az6027.o -dvb-usb-technisat-usb2-objs = technisat-usb2.o +dvb-usb-technisat-usb2-objs := technisat-usb2.o obj-$(CONFIG_DVB_USB_TECHNISAT_USB2) += dvb-usb-technisat-usb2.o ccflags-y += -I$(srctree)/drivers/media/dvb-core diff --git a/drivers/media/usb/em28xx/Makefile b/drivers/media/usb/em28xx/Makefile index 6c5f3381da7..634fb920dd3 100644 --- a/drivers/media/usb/em28xx/Makefile +++ b/drivers/media/usb/em28xx/Makefile @@ -1,4 +1,4 @@ -em28xx-y := em28xx-video.o em28xx-i2c.o em28xx-cards.o +em28xx-y += em28xx-video.o em28xx-i2c.o em28xx-cards.o em28xx-y += em28xx-core.o em28xx-vbi.o em28xx-alsa-objs := em28xx-audio.o diff --git a/drivers/media/usb/pwc/Makefile b/drivers/media/usb/pwc/Makefile index f5c8ec261e8..d7fdbcb9edd 100644 --- a/drivers/media/usb/pwc/Makefile +++ b/drivers/media/usb/pwc/Makefile @@ -1,4 +1,4 @@ -pwc-objs := pwc-if.o pwc-misc.o pwc-ctrl.o pwc-v4l.o pwc-uncompress.o +pwc-objs += pwc-if.o pwc-misc.o pwc-ctrl.o pwc-v4l.o pwc-uncompress.o pwc-objs += pwc-dec1.o pwc-dec23.o pwc-kiara.o pwc-timon.o obj-$(CONFIG_USB_PWC) += pwc.o diff --git a/drivers/media/usb/tm6000/Makefile b/drivers/media/usb/tm6000/Makefile index 6fa1f104451..f2644933b8d 100644 --- a/drivers/media/usb/tm6000/Makefile +++ b/drivers/media/usb/tm6000/Makefile @@ -9,7 +9,7 @@ obj-$(CONFIG_VIDEO_TM6000) += tm6000.o obj-$(CONFIG_VIDEO_TM6000_ALSA) += tm6000-alsa.o obj-$(CONFIG_VIDEO_TM6000_DVB) += tm6000-dvb.o -ccflags-y := -Idrivers/media/i2c +ccflags-y += -Idrivers/media/i2c ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends -- cgit v1.2.3-70-g09d2 From fccea74ff8b5159935acc7b4b4857ee81ee44661 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 20 Aug 2012 14:48:02 -0300 Subject: [media] Kconfig: merge all customise options into just one Instead of having 3 options to allow customizing the media sub-drivers (tuners, I2C drivers, frontends), merge all of them into just one. That simplifies the life for users, as they can just keep this untouched. Life for developers is also simpler, as there's now just one Kconfig item to remember, for the ancillary sub-drivers providing supports for chips that could change from one board design to another. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/Kconfig | 19 +++- drivers/media/common/b2c2/Kconfig | 28 ++--- drivers/media/dvb-frontends/Kconfig | 195 +++++++++++++++------------------ drivers/media/i2c/Kconfig | 21 +--- drivers/media/pci/bt8xx/Kconfig | 24 ++-- drivers/media/pci/cx18/Kconfig | 10 +- drivers/media/pci/cx23885/Kconfig | 34 +++--- drivers/media/pci/cx88/Kconfig | 36 +++--- drivers/media/pci/ddbridge/Kconfig | 10 +- drivers/media/pci/dm1105/Kconfig | 14 +-- drivers/media/pci/mantis/Kconfig | 20 ++-- drivers/media/pci/ngene/Kconfig | 14 +-- drivers/media/pci/saa7134/Kconfig | 40 +++---- drivers/media/pci/saa7146/Kconfig | 8 +- drivers/media/pci/saa7164/Kconfig | 6 +- drivers/media/pci/sta2x11/Kconfig | 2 +- drivers/media/pci/ttpci/Kconfig | 84 +++++++------- drivers/media/pci/zoran/Kconfig | 26 ++--- drivers/media/platform/Kconfig | 2 +- drivers/media/platform/davinci/Kconfig | 4 +- drivers/media/tuners/Kconfig | 88 +++++++-------- drivers/media/usb/au0828/Kconfig | 10 +- drivers/media/usb/cx231xx/Kconfig | 6 +- drivers/media/usb/dvb-usb-v2/Kconfig | 88 +++++++-------- drivers/media/usb/dvb-usb/Kconfig | 148 ++++++++++++------------- drivers/media/usb/em28xx/Kconfig | 28 ++--- drivers/media/usb/pvrusb2/Kconfig | 14 +-- drivers/media/usb/ttusb-budget/Kconfig | 14 +-- drivers/media/usb/usbvision/Kconfig | 2 +- 29 files changed, 488 insertions(+), 507 deletions(-) (limited to 'drivers/media/pci/saa7134') diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 9c3698ab613..dd13e3a4c27 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -162,10 +162,27 @@ source "drivers/media/common/Kconfig" # Ancillary drivers (tuners, i2c, frontends) # +config MEDIA_SUBDRV_AUTOSELECT + bool "Autoselect analog and hybrid tuner modules to build" + depends on MEDIA_TUNER + default y + help + By default, a TV driver auto-selects all possible tuners + thar could be used by the driver. + + This is generally the right thing to do, except when there + are strict constraints with regards to the kernel size. + + Use this option with care, as deselecting tuner drivers which + are in fact necessary will result in TV devices which cannot + be tuned due to lack of the tuning driver. + + If unsure say Y. + comment "Media ancillary drivers (tuners, sensors, i2c, frontends)" -source "drivers/media/tuners/Kconfig" source "drivers/media/i2c/Kconfig" +source "drivers/media/tuners/Kconfig" source "drivers/media/dvb-frontends/Kconfig" endif # MEDIA_SUPPORT diff --git a/drivers/media/common/b2c2/Kconfig b/drivers/media/common/b2c2/Kconfig index e270dd84734..29149de6698 100644 --- a/drivers/media/common/b2c2/Kconfig +++ b/drivers/media/common/b2c2/Kconfig @@ -3,20 +3,20 @@ config DVB_B2C2_FLEXCOP depends on DVB_CORE && I2C depends on DVB_B2C2_FLEXCOP_PCI || DVB_B2C2_FLEXCOP_USB default y - select DVB_PLL if !DVB_FE_CUSTOMISE - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_MT352 if !DVB_FE_CUSTOMISE - select DVB_MT312 if !DVB_FE_CUSTOMISE - select DVB_NXT200X if !DVB_FE_CUSTOMISE - select DVB_STV0297 if !DVB_FE_CUSTOMISE - select DVB_BCM3510 if !DVB_FE_CUSTOMISE - select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_S5H1420 if !DVB_FE_CUSTOMISE - select DVB_TUNER_ITD1000 if !DVB_FE_CUSTOMISE - select DVB_ISL6421 if !DVB_FE_CUSTOMISE - select DVB_CX24123 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE - select DVB_TUNER_CX24113 if !DVB_FE_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MT312 if MEDIA_SUBDRV_AUTOSELECT + select DVB_NXT200X if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0297 if MEDIA_SUBDRV_AUTOSELECT + select DVB_BCM3510 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1420 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TUNER_ITD1000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_ISL6421 if MEDIA_SUBDRV_AUTOSELECT + select DVB_CX24123 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT + select DVB_TUNER_CX24113 if MEDIA_SUBDRV_AUTOSELECT help Support for the digital TV receiver chip made by B2C2 Inc. included in Technisats PCI cards and USB boxes. diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index a08c2152d0e..5efec73a32d 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -1,20 +1,5 @@ -config DVB_FE_CUSTOMISE - bool "Customise the frontend modules to build" - depends on DVB_CORE - depends on EXPERT - default y if EXPERT - help - This allows the user to select/deselect frontend drivers for their - hardware from the build. - - Use this option with care as deselecting frontends which are in fact - necessary will result in DVB devices which cannot be tuned due to lack - of driver support. - - If unsure say N. - menu "Customise DVB Frontends" - visible if DVB_FE_CUSTOMISE + visible if !MEDIA_SUBDRV_AUTOSELECT comment "Multistandard (satellite) frontends" depends on DVB_CORE @@ -22,7 +7,7 @@ comment "Multistandard (satellite) frontends" config DVB_STB0899 tristate "STB0899 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S/S2/DSS Multistandard demodulator. Say Y when you want to support this demodulator based frontends @@ -30,7 +15,7 @@ config DVB_STB0899 config DVB_STB6100 tristate "STB6100 based tuners" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A Silicon tuner from ST used in conjunction with the STB0899 demodulator. Say Y when you want to support this tuner. @@ -38,7 +23,7 @@ config DVB_STB6100 config DVB_STV090x tristate "STV0900/STV0903(A/B) based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help DVB-S/S2/DSS Multistandard Professional/Broadcast demodulators. Say Y when you want to support these frontends. @@ -46,7 +31,7 @@ config DVB_STV090x config DVB_STV6110x tristate "STV6110/(A) based tuners" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A Silicon tuner that supports DVB-S and DVB-S2 modes @@ -56,7 +41,7 @@ comment "Multistandard (cable + terrestrial) frontends" config DVB_DRXK tristate "Micronas DRXK based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Micronas DRX-K DVB-C/T demodulator. @@ -65,7 +50,7 @@ config DVB_DRXK config DVB_TDA18271C2DD tristate "NXP TDA18271C2 silicon tuner" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help NXP TDA18271 silicon tuner. @@ -77,119 +62,119 @@ comment "DVB-S (satellite) frontends" config DVB_CX24110 tristate "Conexant CX24110 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_CX24123 tristate "Conexant CX24123 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_MT312 tristate "Zarlink VP310/MT312/ZL10313 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_ZL10036 tristate "Zarlink ZL10036 silicon tuner" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_ZL10039 tristate "Zarlink ZL10039 silicon tuner" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_S5H1420 tristate "Samsung S5H1420 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_STV0288 tristate "ST STV0288 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_STB6000 tristate "ST STB6000 silicon tuner" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S silicon tuner module. Say Y when you want to support this tuner. config DVB_STV0299 tristate "ST STV0299 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_STV6110 tristate "ST STV6110 silicon tuner" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S silicon tuner module. Say Y when you want to support this tuner. config DVB_STV0900 tristate "ST STV0900 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S/S2 demodulator. Say Y when you want to support this frontend. config DVB_TDA8083 tristate "Philips TDA8083 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_TDA10086 tristate "Philips TDA10086 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_TDA8261 tristate "Philips TDA8261 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_VES1X93 tristate "VLSI VES1893 or VES1993 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_TUNER_ITD1000 tristate "Integrant ITD1000 Zero IF tuner for DVB-S/DSS" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_TUNER_CX24113 tristate "Conexant CX24113/CX24128 tuner for DVB-S/DSS" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. @@ -197,42 +182,42 @@ config DVB_TUNER_CX24113 config DVB_TDA826X tristate "Philips TDA826X silicon tuner" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S silicon tuner module. Say Y when you want to support this tuner. config DVB_TUA6100 tristate "Infineon TUA6100 PLL" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S PLL chip. config DVB_CX24116 tristate "Conexant CX24116 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S/S2 tuner module. Say Y when you want to support this frontend. config DVB_SI21XX tristate "Silicon Labs SI21XX based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_DS3000 tristate "Montage Tehnology DS3000 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S/S2 tuner module. Say Y when you want to support this frontend. config DVB_MB86A16 tristate "Fujitsu MB86A16 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S/DSS Direct Conversion reveiver. Say Y when you want to support this frontend. @@ -240,7 +225,7 @@ config DVB_MB86A16 config DVB_TDA10071 tristate "NXP TDA10071" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y when you want to support this frontend. @@ -250,7 +235,7 @@ comment "DVB-T (terrestrial) frontends" config DVB_SP8870 tristate "Spase sp8870 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Say Y when you want to support this frontend. @@ -262,7 +247,7 @@ config DVB_SP8870 config DVB_SP887X tristate "Spase sp887x based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Say Y when you want to support this frontend. @@ -274,28 +259,28 @@ config DVB_SP887X config DVB_CX22700 tristate "Conexant CX22700 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Say Y when you want to support this frontend. config DVB_CX22702 tristate "Conexant cx22702 demodulator (OFDM)" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Say Y when you want to support this frontend. config DVB_S5H1432 tristate "Samsung s5h1432 demodulator (OFDM)" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Say Y when you want to support this frontend. config DVB_DRXD tristate "Micronas DRXD driver" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Say Y when you want to support this frontend. @@ -306,14 +291,14 @@ config DVB_DRXD config DVB_L64781 tristate "LSI L64781" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Say Y when you want to support this frontend. config DVB_TDA1004X tristate "Philips TDA10045H/TDA10046H based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Say Y when you want to support this frontend. @@ -326,28 +311,28 @@ config DVB_TDA1004X config DVB_NXT6000 tristate "NxtWave Communications NXT6000 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Say Y when you want to support this frontend. config DVB_MT352 tristate "Zarlink MT352 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Say Y when you want to support this frontend. config DVB_ZL10353 tristate "Zarlink ZL10353 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Say Y when you want to support this frontend. config DVB_DIB3000MB tristate "DiBcom 3000M-B" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Designed for mobile usage. Say Y when you want to support this frontend. @@ -355,7 +340,7 @@ config DVB_DIB3000MB config DVB_DIB3000MC tristate "DiBcom 3000P/M-C" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Designed for mobile usage. Say Y when you want to support this frontend. @@ -363,7 +348,7 @@ config DVB_DIB3000MC config DVB_DIB7000M tristate "DiBcom 7000MA/MB/PA/PB/MC" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Designed for mobile usage. Say Y when you want to support this frontend. @@ -371,7 +356,7 @@ config DVB_DIB7000M config DVB_DIB7000P tristate "DiBcom 7000PC" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Designed for mobile usage. Say Y when you want to support this frontend. @@ -379,7 +364,7 @@ config DVB_DIB7000P config DVB_DIB9000 tristate "DiBcom 9000" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Designed for mobile usage. Say Y when you want to support this frontend. @@ -387,56 +372,56 @@ config DVB_DIB9000 config DVB_TDA10048 tristate "Philips TDA10048HN based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Say Y when you want to support this frontend. config DVB_AF9013 tristate "Afatech AF9013 demodulator" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y when you want to support this frontend. config DVB_EC100 tristate "E3C EC100" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y when you want to support this frontend. config DVB_HD29L2 tristate "HDIC HD29L2" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y when you want to support this frontend. config DVB_STV0367 tristate "ST STV0367 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T/C tuner module. Say Y when you want to support this frontend. config DVB_CXD2820R tristate "Sony CXD2820R" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y when you want to support this frontend. config DVB_RTL2830 tristate "Realtek RTL2830 DVB-T" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y when you want to support this frontend. config DVB_RTL2832 tristate "Realtek RTL2832 DVB-T" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y when you want to support this frontend. @@ -446,28 +431,28 @@ comment "DVB-C (cable) frontends" config DVB_VES1820 tristate "VLSI VES1820 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-C tuner module. Say Y when you want to support this frontend. config DVB_TDA10021 tristate "Philips TDA10021 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-C tuner module. Say Y when you want to support this frontend. config DVB_TDA10023 tristate "Philips TDA10023 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-C tuner module. Say Y when you want to support this frontend. config DVB_STV0297 tristate "ST STV0297 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-C tuner module. Say Y when you want to support this frontend. @@ -477,7 +462,7 @@ comment "ATSC (North American/Korean Terrestrial/Cable DTV) frontends" config DVB_NXT200X tristate "NxtWave Communications NXT2002/NXT2004 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. @@ -491,7 +476,7 @@ config DVB_NXT200X config DVB_OR51211 tristate "Oren OR51211 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An ATSC 8VSB tuner module. Say Y when you want to support this frontend. @@ -503,7 +488,7 @@ config DVB_OR51211 config DVB_OR51132 tristate "Oren OR51132 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. @@ -518,7 +503,7 @@ config DVB_OR51132 config DVB_BCM3510 tristate "Broadcom BCM3510" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An ATSC 8VSB/16VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. @@ -526,7 +511,7 @@ config DVB_BCM3510 config DVB_LGDT330X tristate "LG Electronics LGDT3302/LGDT3303 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. @@ -534,7 +519,7 @@ config DVB_LGDT330X config DVB_LGDT3305 tristate "LG Electronics LGDT3304 and LGDT3305 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. @@ -542,7 +527,7 @@ config DVB_LGDT3305 config DVB_LG2160 tristate "LG Electronics LG216x based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An ATSC/MH demodulator module. Say Y when you want to support this frontend. @@ -550,7 +535,7 @@ config DVB_LG2160 config DVB_S5H1409 tristate "Samsung S5H1409 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. @@ -563,7 +548,7 @@ config DVB_AU8522_DTV tristate "Auvitek AU8522 based DTV demod" depends on DVB_CORE && I2C select DVB_AU8522 - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An ATSC 8VSB, QAM64/256 & NTSC demodulator module. Say Y when you want to enable DTV demodulation support for this frontend. @@ -572,7 +557,7 @@ config DVB_AU8522_V4L tristate "Auvitek AU8522 based ATV demod" depends on VIDEO_V4L2 && I2C select DVB_AU8522 - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An ATSC 8VSB, QAM64/256 & NTSC demodulator module. Say Y when you want to enable ATV demodulation support for this frontend. @@ -580,7 +565,7 @@ config DVB_AU8522_V4L config DVB_S5H1411 tristate "Samsung S5H1411 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. @@ -591,7 +576,7 @@ comment "ISDB-T (terrestrial) frontends" config DVB_S921 tristate "Sharp S921 frontend" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help AN ISDB-T DQPSK, QPSK, 16QAM and 64QAM 1seg tuner module. Say Y when you want to support this frontend. @@ -599,7 +584,7 @@ config DVB_S921 config DVB_DIB8000 tristate "DiBcom 8000MB/MC" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A driver for DiBcom's DiB8000 ISDB-T/ISDB-Tsb demodulator. Say Y when you want to support this frontend. @@ -607,7 +592,7 @@ config DVB_DIB8000 config DVB_MB86A20S tristate "Fujitsu mb86a20s" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A driver for Fujitsu mb86a20s ISDB-T/ISDB-Tsb demodulator. Say Y when you want to support this frontend. @@ -618,7 +603,7 @@ comment "Digital terrestrial only tuners/PLL" config DVB_PLL tristate "Generic I2C PLL based tuners" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help This module drives a number of tuners based on PLL chips with a common I2C interface. Say Y when you want to support these tuners. @@ -626,7 +611,7 @@ config DVB_PLL config DVB_TUNER_DIB0070 tristate "DiBcom DiB0070 silicon base-band tuner" depends on I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A driver for the silicon baseband tuner DiB0070 from DiBcom. This device is only used inside a SiP called together with a @@ -635,7 +620,7 @@ config DVB_TUNER_DIB0070 config DVB_TUNER_DIB0090 tristate "DiBcom DiB0090 silicon base-band tuner" depends on I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A driver for the silicon baseband tuner DiB0090 from DiBcom. This device is only used inside a SiP called together with a @@ -647,14 +632,14 @@ comment "SEC control devices for DVB-S" config DVB_LNBP21 tristate "LNBP21/LNBH24 SEC controllers" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An SEC control chips. config DVB_LNBP22 tristate "LNBP22 SEC controllers" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help LNB power supply and control voltage regulator chip with step-up converter @@ -664,33 +649,33 @@ config DVB_LNBP22 config DVB_ISL6405 tristate "ISL6405 SEC controller" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An SEC control chip. config DVB_ISL6421 tristate "ISL6421 SEC controller" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An SEC control chip. config DVB_ISL6423 tristate "ISL6423 SEC controller" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A SEC controller chip from Intersil config DVB_A8293 tristate "Allegro A8293" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT config DVB_LGS8GL5 tristate "Silicon Legend LGS-8GL5 demodulator (OFDM)" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DMB-TH tuner module. Say Y when you want to support this frontend. @@ -698,21 +683,21 @@ config DVB_LGS8GXX tristate "Legend Silicon LGS8913/LGS8GL5/LGS8GXX DMB-TH demodulator" depends on DVB_CORE && I2C select FW_LOADER - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DMB-TH tuner module. Say Y when you want to support this frontend. config DVB_ATBM8830 tristate "AltoBeam ATBM8830/8831 DMB-TH demodulator" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DMB-TH tuner module. Say Y when you want to support this frontend. config DVB_TDA665x tristate "TDA665x tuner" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Support for tuner modules based on Philips TDA6650/TDA6651 chips. Say Y when you want to support this chip. @@ -723,14 +708,14 @@ config DVB_TDA665x config DVB_IX2505V tristate "Sharp IX2505V silicon tuner" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_IT913X_FE tristate "it913x frontend and it9137 tuner" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Say Y when you want to support this frontend. @@ -738,7 +723,7 @@ config DVB_IT913X_FE config DVB_M88RS2000 tristate "M88RS2000 DVB-S demodulator and tuner" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. @@ -746,7 +731,7 @@ config DVB_M88RS2000 config DVB_AF9033 tristate "Afatech AF9033 DVB-T demodulator" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT comment "Tools to develop new frontends" diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 527430ac06f..d41dc0aa005 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -16,21 +16,8 @@ config VIDEO_TVEEPROM if VIDEO_V4L2 -config VIDEO_HELPER_CHIPS_AUTO - bool "Autoselect pertinent encoders/decoders and other helper chips" - default y if !EXPERT - ---help--- - Most video cards may require additional modules to encode or - decode audio/video standards. This option will autoselect - all pertinent modules to each selected video module. - - Unselect this only if you know exactly what you are doing, since - it may break support on some boards. - - In doubt, say Y. - config VIDEO_IR_I2C - tristate "I2C module for IR" if !VIDEO_HELPER_CHIPS_AUTO + tristate "I2C module for IR" if !MEDIA_SUBDRV_AUTOSELECT depends on I2C && RC_CORE default y ---help--- @@ -47,7 +34,7 @@ config VIDEO_IR_I2C # menu "Encoders, decoders, sensors and other helper chips" - visible if !VIDEO_HELPER_CHIPS_AUTO + visible if !MEDIA_SUBDRV_AUTOSELECT comment "Audio decoders, processors and mixers" @@ -561,10 +548,14 @@ config VIDEO_M52790 To compile this driver as a module, choose M here: the module will be called m52790. +endmenu + +menu "Sensors used on soc_camera driver" if SOC_CAMERA source "drivers/media/i2c/soc_camera/Kconfig" endif endmenu + endif diff --git a/drivers/media/pci/bt8xx/Kconfig b/drivers/media/pci/bt8xx/Kconfig index f2667a5cc14..61d09e01081 100644 --- a/drivers/media/pci/bt8xx/Kconfig +++ b/drivers/media/pci/bt8xx/Kconfig @@ -7,10 +7,10 @@ config VIDEO_BT848 depends on RC_CORE select VIDEO_TUNER select VIDEO_TVEEPROM - select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_TVAUDIO if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_TDA7432 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_SAA6588 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_MSP3400 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_TVAUDIO if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_TDA7432 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_SAA6588 if MEDIA_SUBDRV_AUTOSELECT ---help--- Support for BT848 based frame grabber/overlay boards. This includes the Miro, Hauppauge and STB boards. Please read the material in @@ -22,14 +22,14 @@ config VIDEO_BT848 config DVB_BT8XX tristate "DVB/ATSC Support for bt878 based TV cards" depends on DVB_CORE && PCI && I2C && VIDEO_BT848 - select DVB_MT352 if !DVB_FE_CUSTOMISE - select DVB_SP887X if !DVB_FE_CUSTOMISE - select DVB_NXT6000 if !DVB_FE_CUSTOMISE - select DVB_CX24110 if !DVB_FE_CUSTOMISE - select DVB_OR51211 if !DVB_FE_CUSTOMISE - select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE + select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT + select DVB_SP887X if MEDIA_SUBDRV_AUTOSELECT + select DVB_NXT6000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_CX24110 if MEDIA_SUBDRV_AUTOSELECT + select DVB_OR51211 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT + select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT help Support for PCI cards based on the Bt8xx PCI bridge. Examples are the Nebula cards, the Pinnacle PCTV cards, the Twinhan DST cards, diff --git a/drivers/media/pci/cx18/Kconfig b/drivers/media/pci/cx18/Kconfig index 53b3c770257..9a9f765dad4 100644 --- a/drivers/media/pci/cx18/Kconfig +++ b/drivers/media/pci/cx18/Kconfig @@ -8,11 +8,11 @@ config VIDEO_CX18 select VIDEO_TVEEPROM select VIDEO_CX2341X select VIDEO_CS5345 - select DVB_S5H1409 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE - select DVB_S5H1411 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE + select DVB_S5H1409 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT ---help--- This is a video4linux driver for Conexant cx23418 based PCI combo video recorder devices. diff --git a/drivers/media/pci/cx23885/Kconfig b/drivers/media/pci/cx23885/Kconfig index b391e9bda87..9c6afe92258 100644 --- a/drivers/media/pci/cx23885/Kconfig +++ b/drivers/media/pci/cx23885/Kconfig @@ -11,23 +11,23 @@ config VIDEO_CX23885 select VIDEOBUF_DMA_SG select VIDEO_CX25840 select VIDEO_CX2341X - select DVB_DIB7000P if !DVB_FE_CUSTOMISE - select DVB_S5H1409 if !DVB_FE_CUSTOMISE - select DVB_S5H1411 if !DVB_FE_CUSTOMISE - select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select DVB_TDA10048 if !DVB_FE_CUSTOMISE - select DVB_LNBP21 if !DVB_FE_CUSTOMISE - select DVB_STV6110 if !DVB_FE_CUSTOMISE - select DVB_CX24116 if !DVB_FE_CUSTOMISE - select DVB_STV0900 if !DVB_FE_CUSTOMISE - select DVB_DS3000 if !DVB_FE_CUSTOMISE - select DVB_STV0367 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE + select DVB_DIB7000P if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1409 if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT + select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10048 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT + select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT + select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0367 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MT2131 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT ---help--- This is a video4linux driver for Conexant 23885 based TV cards. diff --git a/drivers/media/pci/cx88/Kconfig b/drivers/media/pci/cx88/Kconfig index 3598dc087b0..d27fccbf03c 100644 --- a/drivers/media/pci/cx88/Kconfig +++ b/drivers/media/pci/cx88/Kconfig @@ -6,7 +6,7 @@ config VIDEO_CX88 select VIDEOBUF_DMA_SG select VIDEO_TUNER select VIDEO_TVEEPROM - select VIDEO_WM8775 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_WM8775 if MEDIA_SUBDRV_AUTOSELECT ---help--- This is a video4linux driver for Conexant 2388x based TV cards. @@ -46,23 +46,23 @@ config VIDEO_CX88_DVB tristate "DVB/ATSC Support for cx2388x based TV cards" depends on VIDEO_CX88 && DVB_CORE select VIDEOBUF_DVB - select DVB_PLL if !DVB_FE_CUSTOMISE - select DVB_MT352 if !DVB_FE_CUSTOMISE - select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select DVB_OR51132 if !DVB_FE_CUSTOMISE - select DVB_CX22702 if !DVB_FE_CUSTOMISE - select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_NXT200X if !DVB_FE_CUSTOMISE - select DVB_CX24123 if !DVB_FE_CUSTOMISE - select DVB_ISL6421 if !DVB_FE_CUSTOMISE - select DVB_S5H1411 if !DVB_FE_CUSTOMISE - select DVB_CX24116 if !DVB_FE_CUSTOMISE - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_STV0288 if !DVB_FE_CUSTOMISE - select DVB_STB6000 if !DVB_FE_CUSTOMISE - select DVB_STV0900 if !DVB_FE_CUSTOMISE - select DVB_STB6100 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT + select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT + select DVB_OR51132 if MEDIA_SUBDRV_AUTOSELECT + select DVB_CX22702 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT + select DVB_NXT200X if MEDIA_SUBDRV_AUTOSELECT + select DVB_CX24123 if MEDIA_SUBDRV_AUTOSELECT + select DVB_ISL6421 if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT + select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT ---help--- This adds support for DVB/ATSC cards based on the Conexant 2388x chip. diff --git a/drivers/media/pci/ddbridge/Kconfig b/drivers/media/pci/ddbridge/Kconfig index d099e1a12c8..44e5dc15e60 100644 --- a/drivers/media/pci/ddbridge/Kconfig +++ b/drivers/media/pci/ddbridge/Kconfig @@ -1,11 +1,11 @@ config DVB_DDBRIDGE tristate "Digital Devices bridge support" depends on DVB_CORE && PCI && I2C - select DVB_LNBP21 if !DVB_FE_CUSTOMISE - select DVB_STV6110x if !DVB_FE_CUSTOMISE - select DVB_STV090x if !DVB_FE_CUSTOMISE - select DVB_DRXK if !DVB_FE_CUSTOMISE - select DVB_TDA18271C2DD if !DVB_FE_CUSTOMISE + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV6110x if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT + select DVB_DRXK if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT ---help--- Support for cards with the Digital Devices PCI express bridge: - Octopus PCIe Bridge diff --git a/drivers/media/pci/dm1105/Kconfig b/drivers/media/pci/dm1105/Kconfig index f3de0a4d63f..013df4e015c 100644 --- a/drivers/media/pci/dm1105/Kconfig +++ b/drivers/media/pci/dm1105/Kconfig @@ -1,13 +1,13 @@ config DVB_DM1105 tristate "SDMC DM1105 based PCI cards" depends on DVB_CORE && PCI && I2C - select DVB_PLL if !DVB_FE_CUSTOMISE - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_STV0288 if !DVB_FE_CUSTOMISE - select DVB_STB6000 if !DVB_FE_CUSTOMISE - select DVB_CX24116 if !DVB_FE_CUSTOMISE - select DVB_SI21XX if !DVB_FE_CUSTOMISE - select DVB_DS3000 if !DVB_FE_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT + select DVB_SI21XX if MEDIA_SUBDRV_AUTOSELECT + select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT depends on RC_CORE help Support for cards based on the SDMC DM1105 PCI chip like diff --git a/drivers/media/pci/mantis/Kconfig b/drivers/media/pci/mantis/Kconfig index a13a5050313..d3cc21633b9 100644 --- a/drivers/media/pci/mantis/Kconfig +++ b/drivers/media/pci/mantis/Kconfig @@ -10,15 +10,15 @@ config MANTIS_CORE config DVB_MANTIS tristate "MANTIS based cards" depends on MANTIS_CORE && DVB_CORE && PCI && I2C - select DVB_MB86A16 if !DVB_FE_CUSTOMISE - select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_LNBP21 if !DVB_FE_CUSTOMISE - select DVB_STB0899 if !DVB_FE_CUSTOMISE - select DVB_STB6100 if !DVB_FE_CUSTOMISE - select DVB_TDA665x if !DVB_FE_CUSTOMISE - select DVB_TDA10021 if !DVB_FE_CUSTOMISE - select DVB_TDA10023 if !DVB_FE_CUSTOMISE + select DVB_MB86A16 if MEDIA_SUBDRV_AUTOSELECT + select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA665x if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10021 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT select DVB_PLL help Support for PCI cards based on the Mantis PCI bridge. @@ -29,7 +29,7 @@ config DVB_MANTIS config DVB_HOPPER tristate "HOPPER based cards" depends on MANTIS_CORE && DVB_CORE && PCI && I2C - select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT select DVB_PLL help Support for PCI cards based on the Hopper PCI bridge. diff --git a/drivers/media/pci/ngene/Kconfig b/drivers/media/pci/ngene/Kconfig index 64c84702ba5..637d506b23c 100644 --- a/drivers/media/pci/ngene/Kconfig +++ b/drivers/media/pci/ngene/Kconfig @@ -1,13 +1,13 @@ config DVB_NGENE tristate "Micronas nGene support" depends on DVB_CORE && PCI && I2C - select DVB_LNBP21 if !DVB_FE_CUSTOMISE - select DVB_STV6110x if !DVB_FE_CUSTOMISE - select DVB_STV090x if !DVB_FE_CUSTOMISE - select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_DRXK if !DVB_FE_CUSTOMISE - select DVB_TDA18271C2DD if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMISE + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV6110x if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT + select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT + select DVB_DRXK if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MT2131 if MEDIA_SUBDRV_AUTOSELECT ---help--- Support for Micronas PCI express cards with nGene bridge. diff --git a/drivers/media/pci/saa7134/Kconfig b/drivers/media/pci/saa7134/Kconfig index 39fc0187a74..15b90d6e913 100644 --- a/drivers/media/pci/saa7134/Kconfig +++ b/drivers/media/pci/saa7134/Kconfig @@ -5,7 +5,7 @@ config VIDEO_SAA7134 select VIDEO_TUNER select VIDEO_TVEEPROM select CRC32 - select VIDEO_SAA6588 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_SAA6588 if MEDIA_SUBDRV_AUTOSELECT ---help--- This is a video4linux driver for Philips SAA713x based TV cards. @@ -37,25 +37,25 @@ 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 !DVB_FE_CUSTOMISE - select DVB_MT352 if !DVB_FE_CUSTOMISE - select DVB_TDA1004X if !DVB_FE_CUSTOMISE - select DVB_NXT200X if !DVB_FE_CUSTOMISE - select DVB_TDA10086 if !DVB_FE_CUSTOMISE - select DVB_TDA826X if !DVB_FE_CUSTOMISE - select DVB_ISL6421 if !DVB_FE_CUSTOMISE - select DVB_ISL6405 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE - select DVB_ZL10036 if !DVB_FE_CUSTOMISE - select DVB_MT312 if !DVB_FE_CUSTOMISE - select DVB_LNBP21 if !DVB_FE_CUSTOMISE - select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select DVB_LGDT3305 if !DVB_FE_CUSTOMISE - select DVB_TDA10048 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE - select DVB_ZL10039 if !DVB_FE_CUSTOMISE + 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. diff --git a/drivers/media/pci/saa7146/Kconfig b/drivers/media/pci/saa7146/Kconfig index 8923b762bba..da88b77a916 100644 --- a/drivers/media/pci/saa7146/Kconfig +++ b/drivers/media/pci/saa7146/Kconfig @@ -26,10 +26,10 @@ config VIDEO_MXB depends on PCI && VIDEO_V4L2 && I2C select VIDEO_SAA7146_VV select VIDEO_TUNER - select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_TDA9840 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_TEA6415C if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_TEA6420 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_TDA9840 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_TEA6415C if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_TEA6420 if MEDIA_SUBDRV_AUTOSELECT ---help--- This is a video4linux driver for the 'Multimedia eXtension Board' TV card by Siemens-Nixdorf. diff --git a/drivers/media/pci/saa7164/Kconfig b/drivers/media/pci/saa7164/Kconfig index 35326372517..84637965287 100644 --- a/drivers/media/pci/saa7164/Kconfig +++ b/drivers/media/pci/saa7164/Kconfig @@ -6,9 +6,9 @@ config VIDEO_SAA7164 select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEOBUF_DVB - select DVB_TDA10048 if !DVB_FE_CUSTOMISE - select DVB_S5H1411 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE + select DVB_TDA10048 if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT ---help--- This is a video4linux driver for NXP SAA7164 based TV cards. diff --git a/drivers/media/pci/sta2x11/Kconfig b/drivers/media/pci/sta2x11/Kconfig index 04a82cbd7c9..6749f67cab8 100644 --- a/drivers/media/pci/sta2x11/Kconfig +++ b/drivers/media/pci/sta2x11/Kconfig @@ -1,7 +1,7 @@ config STA2X11_VIP tristate "STA2X11 VIP Video For Linux" depends on STA2X11 - select VIDEO_ADV7180 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_ADV7180 if MEDIA_SUBDRV_AUTOSELECT select VIDEOBUF_DMA_CONTIG depends on PCI && VIDEO_V4L2 && VIRT_TO_BUS help diff --git a/drivers/media/pci/ttpci/Kconfig b/drivers/media/pci/ttpci/Kconfig index 9d83ced69dd..314e417adda 100644 --- a/drivers/media/pci/ttpci/Kconfig +++ b/drivers/media/pci/ttpci/Kconfig @@ -9,14 +9,14 @@ config DVB_AV7110 select TTPCI_EEPROM select VIDEO_SAA7146_VV depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV - select DVB_VES1820 if !DVB_FE_CUSTOMISE - select DVB_VES1X93 if !DVB_FE_CUSTOMISE - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_TDA8083 if !DVB_FE_CUSTOMISE - select DVB_SP8870 if !DVB_FE_CUSTOMISE - select DVB_STV0297 if !DVB_FE_CUSTOMISE - select DVB_L64781 if !DVB_FE_CUSTOMISE - select DVB_LNBP21 if !DVB_FE_CUSTOMISE + select DVB_VES1820 if MEDIA_SUBDRV_AUTOSELECT + select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT + select DVB_SP8870 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0297 if MEDIA_SUBDRV_AUTOSELECT + select DVB_L64781 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT help Support for SAA7146 and AV7110 based DVB cards as produced by Fujitsu-Siemens, Technotrend, Hauppauge and others. @@ -63,19 +63,19 @@ config DVB_BUDGET_CORE config DVB_BUDGET tristate "Budget cards" depends on DVB_BUDGET_CORE && I2C - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_VES1X93 if !DVB_FE_CUSTOMISE - select DVB_VES1820 if !DVB_FE_CUSTOMISE - select DVB_L64781 if !DVB_FE_CUSTOMISE - select DVB_TDA8083 if !DVB_FE_CUSTOMISE - select DVB_S5H1420 if !DVB_FE_CUSTOMISE - select DVB_TDA10086 if !DVB_FE_CUSTOMISE - select DVB_TDA826X if !DVB_FE_CUSTOMISE - select DVB_LNBP21 if !DVB_FE_CUSTOMISE - select DVB_TDA1004X if !DVB_FE_CUSTOMISE - select DVB_ISL6423 if !DVB_FE_CUSTOMISE - select DVB_STV090x if !DVB_FE_CUSTOMISE - select DVB_STV6110x if !DVB_FE_CUSTOMISE + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT + select DVB_VES1820 if MEDIA_SUBDRV_AUTOSELECT + select DVB_L64781 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1420 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT + select DVB_ISL6423 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV6110x if MEDIA_SUBDRV_AUTOSELECT help Support for simple SAA7146 based DVB cards (so called Budget- or Nova-PCI cards) without onboard MPEG2 decoder, and without @@ -89,16 +89,16 @@ config DVB_BUDGET config DVB_BUDGET_CI tristate "Budget cards with onboard CI connector" depends on DVB_BUDGET_CORE && I2C - select DVB_STV0297 if !DVB_FE_CUSTOMISE - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_TDA1004X if !DVB_FE_CUSTOMISE - select DVB_STB0899 if !DVB_FE_CUSTOMISE - select DVB_STB6100 if !DVB_FE_CUSTOMISE - select DVB_LNBP21 if !DVB_FE_CUSTOMISE - select DVB_STV0288 if !DVB_FE_CUSTOMISE - select DVB_STB6000 if !DVB_FE_CUSTOMISE - select DVB_TDA10023 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMISE + select DVB_STV0297 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA827X if MEDIA_SUBDRV_AUTOSELECT depends on RC_CORE help Support for simple SAA7146 based DVB cards @@ -118,14 +118,14 @@ config DVB_BUDGET_AV depends on DVB_BUDGET_CORE && I2C select VIDEO_SAA7146_VV depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV - select DVB_PLL if !DVB_FE_CUSTOMISE - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_TDA1004X if !DVB_FE_CUSTOMISE - select DVB_TDA10021 if !DVB_FE_CUSTOMISE - select DVB_TDA10023 if !DVB_FE_CUSTOMISE - select DVB_STB0899 if !DVB_FE_CUSTOMISE - select DVB_TDA8261 if !DVB_FE_CUSTOMISE - select DVB_TUA6100 if !DVB_FE_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10021 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA8261 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TUA6100 if MEDIA_SUBDRV_AUTOSELECT help Support for simple SAA7146 based DVB cards (so called Budget- or Nova-PCI cards) without onboard @@ -140,9 +140,9 @@ config DVB_BUDGET_PATCH tristate "AV7110 cards with Budget Patch" depends on DVB_BUDGET_CORE && I2C depends on DVB_AV7110 - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_VES1X93 if !DVB_FE_CUSTOMISE - select DVB_TDA8083 if !DVB_FE_CUSTOMISE + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT help Support for Budget Patch (full TS) modification on SAA7146+AV7110 based cards (DVB-S cards). This diff --git a/drivers/media/pci/zoran/Kconfig b/drivers/media/pci/zoran/Kconfig index fd4120e4c10..a9b23180841 100644 --- a/drivers/media/pci/zoran/Kconfig +++ b/drivers/media/pci/zoran/Kconfig @@ -14,8 +14,8 @@ config VIDEO_ZORAN config VIDEO_ZORAN_DC30 tristate "Pinnacle/Miro DC30(+) support" depends on VIDEO_ZORAN - select VIDEO_ADV7175 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_VPX3220 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_ADV7175 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_VPX3220 if MEDIA_SUBDRV_AUTOSELECT help Support for the Pinnacle/Miro DC30(+) MJPEG capture/playback card. This also supports really old DC10 cards based on the @@ -32,16 +32,16 @@ config VIDEO_ZORAN_ZR36060 config VIDEO_ZORAN_BUZ tristate "Iomega Buz support" depends on VIDEO_ZORAN_ZR36060 - select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_SAA7185 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_SAA7185 if MEDIA_SUBDRV_AUTOSELECT help Support for the Iomega Buz MJPEG capture/playback card. config VIDEO_ZORAN_DC10 tristate "Pinnacle/Miro DC10(+) support" depends on VIDEO_ZORAN_ZR36060 - select VIDEO_SAA7110 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_ADV7175 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_SAA7110 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_ADV7175 if MEDIA_SUBDRV_AUTOSELECT help Support for the Pinnacle/Miro DC10(+) MJPEG capture/playback card. @@ -49,8 +49,8 @@ config VIDEO_ZORAN_DC10 config VIDEO_ZORAN_LML33 tristate "Linux Media Labs LML33 support" depends on VIDEO_ZORAN_ZR36060 - select VIDEO_BT819 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_BT856 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_BT819 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_BT856 if MEDIA_SUBDRV_AUTOSELECT help Support for the Linux Media Labs LML33 MJPEG capture/playback card. @@ -58,8 +58,8 @@ config VIDEO_ZORAN_LML33 config VIDEO_ZORAN_LML33R10 tristate "Linux Media Labs LML33R10 support" depends on VIDEO_ZORAN_ZR36060 - select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_ADV7170 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_ADV7170 if MEDIA_SUBDRV_AUTOSELECT help support for the Linux Media Labs LML33R10 MJPEG capture/playback card. @@ -67,8 +67,8 @@ config VIDEO_ZORAN_LML33R10 config VIDEO_ZORAN_AVS6EYES tristate "AverMedia 6 Eyes support (EXPERIMENTAL)" depends on VIDEO_ZORAN_ZR36060 && EXPERIMENTAL - select VIDEO_BT856 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_BT866 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_KS0127 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_BT856 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_BT866 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_KS0127 if MEDIA_SUBDRV_AUTOSELECT help Support for the AverMedia 6 Eyes video surveillance card. diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 5955a276f46..d4c034d2bd8 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -66,7 +66,7 @@ config VIDEO_TIMBERDALE config VIDEO_VINO tristate "SGI Vino Video For Linux" depends on I2C && SGI_IP22 && VIDEO_V4L2 - select VIDEO_SAA7191 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_SAA7191 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to build in support for the Vino video input system found on SGI Indy machines. diff --git a/drivers/media/platform/davinci/Kconfig b/drivers/media/platform/davinci/Kconfig index 52c5ca68cb3..78e26d24f63 100644 --- a/drivers/media/platform/davinci/Kconfig +++ b/drivers/media/platform/davinci/Kconfig @@ -3,8 +3,8 @@ config VIDEO_DAVINCI_VPIF_DISPLAY depends on VIDEO_DEV && (MACH_DAVINCI_DM6467_EVM || MACH_DAVINCI_DA850_EVM) select VIDEOBUF2_DMA_CONTIG select VIDEO_DAVINCI_VPIF - select VIDEO_ADV7343 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_THS7303 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_ADV7343 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_THS7303 if MEDIA_SUBDRV_AUTOSELECT help Enables Davinci VPIF module used for display devices. This module is common for following DM6467/DA850/OMAPL138 diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig index 94c6ff7a5da..80238b9063b 100644 --- a/drivers/media/tuners/Kconfig +++ b/drivers/media/tuners/Kconfig @@ -18,43 +18,31 @@ config MEDIA_ATTACH If unsure say Y. +# Analog TV tuners, auto-loaded via tuner.ko config MEDIA_TUNER tristate depends on (MEDIA_ANALOG_TV_SUPPORT || MEDIA_RADIO_SUPPORT) && I2C default y - select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_XC4000 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_MT20XX if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TEA5761 if !MEDIA_TUNER_CUSTOMISE && MEDIA_RADIO_SUPPORT && EXPERIMENTAL - select MEDIA_TUNER_TEA5767 if !MEDIA_TUNER_CUSTOMISE && MEDIA_RADIO_SUPPORT - select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TDA9887 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_MC44S803 if !MEDIA_TUNER_CUSTOMISE - -config MEDIA_TUNER_CUSTOMISE - bool "Customize analog and hybrid tuner modules to build" - depends on MEDIA_TUNER - default y if EXPERT - help - This allows the user to deselect tuner drivers unnecessary - for their hardware from the build. Use this option with care - as deselecting tuner drivers which are in fact necessary will - result in V4L/DVB devices which cannot be tuned due to lack of - driver support - - If unsure say N. + select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_XC4000 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MT20XX if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TEA5761 if MEDIA_SUBDRV_AUTOSELECT && MEDIA_RADIO_SUPPORT && EXPERIMENTAL + select MEDIA_TUNER_TEA5767 if MEDIA_SUBDRV_AUTOSELECT && MEDIA_RADIO_SUPPORT + select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA9887 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MC44S803 if MEDIA_SUBDRV_AUTOSELECT menu "Customize TV tuners" - visible if MEDIA_TUNER_CUSTOMISE + visible if !MEDIA_SUBDRV_AUTOSELECT depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT config MEDIA_TUNER_SIMPLE tristate "Simple tuner support" depends on MEDIA_SUPPORT && I2C select MEDIA_TUNER_TDA9887 - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y here to include support for various simple tuners. @@ -63,28 +51,28 @@ config MEDIA_TUNER_TDA8290 depends on MEDIA_SUPPORT && I2C select MEDIA_TUNER_TDA827X select MEDIA_TUNER_TDA18271 - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y here to include support for Philips TDA8290+8275(a) tuner. config MEDIA_TUNER_TDA827X tristate "Philips TDA827X silicon tuner" depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T silicon tuner module. Say Y when you want to support this tuner. config MEDIA_TUNER_TDA18271 tristate "NXP TDA18271 silicon tuner" depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A silicon tuner module. Say Y when you want to support this tuner. config MEDIA_TUNER_TDA9887 tristate "TDA 9885/6/7 analog IF demodulator" depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y here to include support for Philips TDA9885/6/7 analog IF demodulator. @@ -93,70 +81,70 @@ config MEDIA_TUNER_TEA5761 tristate "TEA 5761 radio tuner (EXPERIMENTAL)" depends on MEDIA_SUPPORT && I2C depends on EXPERIMENTAL - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y here to include support for the Philips TEA5761 radio tuner. config MEDIA_TUNER_TEA5767 tristate "TEA 5767 radio tuner" depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y here to include support for the Philips TEA5767 radio tuner. config MEDIA_TUNER_MT20XX tristate "Microtune 2032 / 2050 tuners" depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y here to include support for the MT2032 / MT2050 tuner. config MEDIA_TUNER_MT2060 tristate "Microtune MT2060 silicon IF tuner" depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A driver for the silicon IF tuner MT2060 from Microtune. config MEDIA_TUNER_MT2063 tristate "Microtune MT2063 silicon IF tuner" depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A driver for the silicon IF tuner MT2063 from Microtune. config MEDIA_TUNER_MT2266 tristate "Microtune MT2266 silicon tuner" depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A driver for the silicon baseband tuner MT2266 from Microtune. config MEDIA_TUNER_MT2131 tristate "Microtune MT2131 silicon tuner" depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A driver for the silicon baseband tuner MT2131 from Microtune. config MEDIA_TUNER_QT1010 tristate "Quantek QT1010 silicon tuner" depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A driver for the silicon tuner QT1010 from Quantek. config MEDIA_TUNER_XC2028 tristate "XCeive xc2028/xc3028 tuners" depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y here to include support for the xc2028/xc3028 tuners. config MEDIA_TUNER_XC5000 tristate "Xceive XC5000 silicon tuner" depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A driver for the silicon tuner XC5000 from Xceive. This device is only used inside a SiP called together with a @@ -165,7 +153,7 @@ config MEDIA_TUNER_XC5000 config MEDIA_TUNER_XC4000 tristate "Xceive XC4000 silicon tuner" depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A driver for the silicon tuner XC4000 from Xceive. This device is only used inside a SiP called together with a @@ -174,70 +162,70 @@ config MEDIA_TUNER_XC4000 config MEDIA_TUNER_MXL5005S tristate "MaxLinear MSL5005S silicon tuner" depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A driver for the silicon tuner MXL5005S from MaxLinear. config MEDIA_TUNER_MXL5007T tristate "MaxLinear MxL5007T silicon tuner" depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A driver for the silicon tuner MxL5007T from MaxLinear. config MEDIA_TUNER_MC44S803 tristate "Freescale MC44S803 Low Power CMOS Broadband tuners" depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Freescale MC44S803 based tuners config MEDIA_TUNER_MAX2165 tristate "Maxim MAX2165 silicon tuner" depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A driver for the silicon tuner MAX2165 from Maxim. config MEDIA_TUNER_TDA18218 tristate "NXP TDA18218 silicon tuner" depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help NXP TDA18218 silicon tuner driver. config MEDIA_TUNER_FC0011 tristate "Fitipower FC0011 silicon tuner" depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Fitipower FC0011 silicon tuner driver. config MEDIA_TUNER_FC0012 tristate "Fitipower FC0012 silicon tuner" depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Fitipower FC0012 silicon tuner driver. config MEDIA_TUNER_FC0013 tristate "Fitipower FC0013 silicon tuner" depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Fitipower FC0013 silicon tuner driver. config MEDIA_TUNER_TDA18212 tristate "NXP TDA18212 silicon tuner" depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help NXP TDA18212 silicon tuner driver. config MEDIA_TUNER_TUA9001 tristate "Infineon TUA 9001 silicon tuner" depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Infineon TUA 9001 silicon tuner driver. endmenu diff --git a/drivers/media/usb/au0828/Kconfig b/drivers/media/usb/au0828/Kconfig index 23f7fd22f0e..385e557ba91 100644 --- a/drivers/media/usb/au0828/Kconfig +++ b/drivers/media/usb/au0828/Kconfig @@ -6,11 +6,11 @@ config VIDEO_AU0828 select I2C_ALGOBIT select VIDEO_TVEEPROM select VIDEOBUF_VMALLOC - select DVB_AU8522_DTV if !DVB_FE_CUSTOMISE - select DVB_AU8522_V4L if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE + select DVB_AU8522_DTV if MEDIA_SUBDRV_AUTOSELECT + select DVB_AU8522_V4L if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT ---help--- This is a video4linux driver for Auvitek's USB device. diff --git a/drivers/media/usb/cx231xx/Kconfig b/drivers/media/usb/cx231xx/Kconfig index 446f692aabb..77913dfbd23 100644 --- a/drivers/media/usb/cx231xx/Kconfig +++ b/drivers/media/usb/cx231xx/Kconfig @@ -42,9 +42,9 @@ config VIDEO_CX231XX_DVB tristate "DVB/ATSC Support for Cx231xx based TV cards" depends on VIDEO_CX231XX && DVB_CORE && DVB_CAPTURE_DRIVERS select VIDEOBUF_DVB - select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE - select DVB_MB86A20S if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MB86A20S if MEDIA_SUBDRV_AUTOSELECT ---help--- This adds support for DVB cards based on the diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig index 276374fbaf4..96711510404 100644 --- a/drivers/media/usb/dvb-usb-v2/Kconfig +++ b/drivers/media/usb/dvb-usb-v2/Kconfig @@ -21,14 +21,14 @@ config DVB_USB_AF9015 tristate "Afatech AF9015 DVB-T USB2.0 support" depends on DVB_USB_V2 select DVB_AF9013 - select DVB_PLL if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_MC44S803 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TDA18218 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MC44S803 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18218 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Afatech AF9015 based DVB-T USB2.0 receiver @@ -36,26 +36,26 @@ config DVB_USB_AF9035 tristate "Afatech AF9035 DVB-T USB2.0 support" depends on DVB_USB_V2 select DVB_AF9033 - select MEDIA_TUNER_TUA9001 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_FC0011 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TDA18218 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_TUA9001 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_FC0011 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18218 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Afatech AF9035 based DVB USB receiver. config DVB_USB_ANYSEE tristate "Anysee DVB-T/C USB2.0 support" depends on DVB_USB_V2 - select DVB_PLL if !DVB_FE_CUSTOMISE - select DVB_MT352 if !DVB_FE_CUSTOMISE - select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select DVB_TDA10023 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA18212 if !MEDIA_TUNER_CUSTOMISE - select DVB_CX24116 if !DVB_FE_CUSTOMISE - select DVB_STV0900 if !DVB_FE_CUSTOMISE - select DVB_STV6110 if !DVB_FE_CUSTOMISE - select DVB_ISL6423 if !DVB_FE_CUSTOMISE - select DVB_CXD2820R if !DVB_FE_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT + select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18212 if MEDIA_SUBDRV_AUTOSELECT + select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT + select DVB_ISL6423 if MEDIA_SUBDRV_AUTOSELECT + select DVB_CXD2820R if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Anysee E30, Anysee E30 Plus or Anysee E30 C Plus DVB USB2.0 receiver. @@ -63,8 +63,8 @@ config DVB_USB_ANYSEE config DVB_USB_AU6610 tristate "Alcor Micro AU6610 USB2.0 support" depends on DVB_USB_V2 - select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE + select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Sigmatek DVB-110 DVB-T USB2.0 receiver. @@ -72,8 +72,8 @@ config DVB_USB_AZ6007 tristate "AzureWave 6007 and clones DVB-T/C USB2.0 support" depends on DVB_USB_V2 select DVB_USB_CYPRESS_FIRMWARE - select DVB_DRXK if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MT2063 if !DVB_FE_CUSTOMISE + select DVB_DRXK if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MT2063 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the AZ6007 receivers like Terratec H7. @@ -81,7 +81,7 @@ config DVB_USB_CE6230 tristate "Intel CE6230 DVB-T USB2.0 support" depends on DVB_USB_V2 select DVB_ZL10353 - select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Intel CE6230 DVB-T USB2.0 receiver @@ -89,15 +89,15 @@ config DVB_USB_EC168 tristate "E3C EC168 DVB-T USB2.0 support" depends on DVB_USB_V2 select DVB_EC100 - select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the E3C EC168 DVB-T USB2.0 receiver. config DVB_USB_GL861 tristate "Genesys Logic GL861 USB2.0 support" depends on DVB_USB_V2 - select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE + select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the MSI Megasky 580 (55801) DVB-T USB2.0 receiver with USB ID 0db0:5581. @@ -112,21 +112,21 @@ config DVB_USB_IT913X config DVB_USB_LME2510 tristate "LME DM04/QQBOX DVB-S USB2.0 support" depends on DVB_USB_V2 - select DVB_TDA10086 if !DVB_FE_CUSTOMISE - select DVB_TDA826X if !DVB_FE_CUSTOMISE - select DVB_STV0288 if !DVB_FE_CUSTOMISE - select DVB_IX2505V if !DVB_FE_CUSTOMISE - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_PLL if !DVB_FE_CUSTOMISE - select DVB_M88RS2000 if !DVB_FE_CUSTOMISE + select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT + select DVB_IX2505V if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select DVB_M88RS2000 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the LME DM04/QQBOX DVB-S USB2.0 config DVB_USB_MXL111SF tristate "MxL111SF DTV USB2.0 support" depends on DVB_USB_V2 - select DVB_LGDT3305 if !DVB_FE_CUSTOMISE - select DVB_LG2160 if !DVB_FE_CUSTOMISE + select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LG2160 if MEDIA_SUBDRV_AUTOSELECT select VIDEO_TVEEPROM help Say Y here to support the MxL111SF USB2.0 DTV receiver. @@ -136,11 +136,11 @@ config DVB_USB_RTL28XXU depends on DVB_USB_V2 && EXPERIMENTAL select DVB_RTL2830 select DVB_RTL2832 - select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_FC0012 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_FC0013 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_FC0012 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_FC0013 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Realtek RTL28xxU DVB USB receiver. diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig index 00173ee15d4..3c5fff89dbf 100644 --- a/drivers/media/usb/dvb-usb/Kconfig +++ b/drivers/media/usb/dvb-usb/Kconfig @@ -24,17 +24,17 @@ config DVB_USB_A800 tristate "AVerMedia AverTV DVB-T USB 2.0 (A800)" depends on DVB_USB select DVB_DIB3000MC - select DVB_PLL if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the AVerMedia AverTV DVB-T USB 2.0 (A800) receiver. config DVB_USB_DIBUSB_MB tristate "DiBcom USB DVB-T devices (based on the DiB3000M-B) (see help for device list)" depends on DVB_USB - select DVB_PLL if !DVB_FE_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT select DVB_DIB3000MB - select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT help Support for USB 1.1 and 2.0 DVB-T receivers based on reference designs made by DiBcom () equipped with a DiB3000M-B demodulator. @@ -55,7 +55,7 @@ config DVB_USB_DIBUSB_MC tristate "DiBcom USB DVB-T devices (based on the DiB3000M-C/P) (see help for device list)" depends on DVB_USB select DVB_DIB3000MC - select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT help Support for USB2.0 DVB-T receivers based on reference designs made by DiBcom () equipped with a DiB3000M-C/P demodulator. @@ -69,20 +69,20 @@ config DVB_USB_DIBUSB_MC config DVB_USB_DIB0700 tristate "DiBcom DiB0700 USB DVB devices (see help for supported devices)" depends on DVB_USB - select DVB_DIB7000P if !DVB_FE_CUSTOMISE - select DVB_DIB7000M if !DVB_FE_CUSTOMISE - select DVB_DIB8000 if !DVB_FE_CUSTOMISE - select DVB_DIB3000MC if !DVB_FE_CUSTOMISE - select DVB_S5H1411 if !DVB_FE_CUSTOMISE - select DVB_LGDT3305 if !DVB_FE_CUSTOMISE - select DVB_TUNER_DIB0070 if !DVB_FE_CUSTOMISE - select DVB_TUNER_DIB0090 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_MT2266 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_XC4000 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE + select DVB_DIB7000P if MEDIA_SUBDRV_AUTOSELECT + select DVB_DIB7000M if MEDIA_SUBDRV_AUTOSELECT + select DVB_DIB8000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_DIB3000MC if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TUNER_DIB0070 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TUNER_DIB0090 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MT2266 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_XC4000 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT help Support for USB2.0/1.1 DVB receivers based on the DiB0700 USB bridge. The USB bridge is also present in devices having the DiB7700 DVB-T-USB @@ -98,29 +98,29 @@ config DVB_USB_DIB0700 config DVB_USB_UMT_010 tristate "HanfTek UMT-010 DVB-T USB2.0 support" depends on DVB_USB - select DVB_PLL if !DVB_FE_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT select DVB_DIB3000MC - select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE - select DVB_MT352 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the HanfTek UMT-010 USB2.0 stick-sized DVB-T receiver. config DVB_USB_CXUSB tristate "Conexant USB2.0 hybrid reference design support" depends on DVB_USB - select DVB_PLL if !DVB_FE_CUSTOMISE - select DVB_CX22702 if !DVB_FE_CUSTOMISE - select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_MT352 if !DVB_FE_CUSTOMISE - select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select DVB_DIB7000P if !DVB_FE_CUSTOMISE - select DVB_TUNER_DIB0070 if !DVB_FE_CUSTOMISE - select DVB_ATBM8830 if !DVB_FE_CUSTOMISE - select DVB_LGS8GXX if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_MAX2165 if !MEDIA_TUNER_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select DVB_CX22702 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT + select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT + select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT + select DVB_DIB7000P if MEDIA_SUBDRV_AUTOSELECT + select DVB_TUNER_DIB0070 if MEDIA_SUBDRV_AUTOSELECT + select DVB_ATBM8830 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LGS8GXX if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MAX2165 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Conexant USB2.0 hybrid reference design. Currently, only DVB and ATSC modes are supported, analog mode @@ -132,11 +132,11 @@ config DVB_USB_CXUSB config DVB_USB_M920X tristate "Uli m920x DVB-T USB2.0 support" depends on DVB_USB - select DVB_MT352 if !DVB_FE_CUSTOMISE - select DVB_TDA1004X if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE + select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA827X if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the MSI Mega Sky 580 USB2.0 DVB-T receiver. Currently, only devices with a product id of @@ -146,9 +146,9 @@ config DVB_USB_M920X config DVB_USB_DIGITV tristate "Nebula Electronics uDigiTV DVB-T USB2.0 support" depends on DVB_USB - select DVB_PLL if !DVB_FE_CUSTOMISE - select DVB_NXT6000 if !DVB_FE_CUSTOMISE - select DVB_MT352 if !DVB_FE_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select DVB_NXT6000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Nebula Electronics uDigitV USB2.0 DVB-T receiver. @@ -191,17 +191,17 @@ config DVB_USB_NOVA_T_USB2 tristate "Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 support" depends on DVB_USB select DVB_DIB3000MC - select DVB_PLL if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 receiver. config DVB_USB_TTUSB2 tristate "Pinnacle 400e DVB-S USB2.0 support" depends on DVB_USB - select DVB_TDA10086 if !DVB_FE_CUSTOMISE - select DVB_LNBP21 if !DVB_FE_CUSTOMISE - select DVB_TDA826X if !DVB_FE_CUSTOMISE + select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Pinnacle 400e DVB-S USB2.0 receiver. The firmware protocol used by this module is similar to the one used by the @@ -220,16 +220,16 @@ config DVB_USB_DTT200U config DVB_USB_OPERA1 tristate "Opera1 DVB-S USB2.0 receiver" depends on DVB_USB - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_PLL if !DVB_FE_CUSTOMISE + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Opera DVB-S USB2.0 receiver. config DVB_USB_AF9005 tristate "Afatech AF9005 DVB-T USB1.1 support" depends on DVB_USB && EXPERIMENTAL - select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Afatech AF9005 based DVB-T USB1.1 receiver and the TerraTec Cinergy T USB XE (Rev.1) @@ -245,9 +245,9 @@ config DVB_USB_PCTV452E tristate "Pinnacle PCTV HDTV Pro USB device/TT Connect S2-3600" depends on DVB_USB select TTPCI_EEPROM - select DVB_LNBP22 if !DVB_FE_CUSTOMISE - select DVB_STB0899 if !DVB_FE_CUSTOMISE - select DVB_STB6100 if !DVB_FE_CUSTOMISE + select DVB_LNBP22 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT help Support for external USB adapter designed by Pinnacle, shipped under the brand name 'PCTV HDTV Pro USB'. @@ -257,19 +257,19 @@ config DVB_USB_PCTV452E config DVB_USB_DW2102 tristate "DvbWorld & TeVii DVB-S/S2 USB2.0 support" depends on DVB_USB - select DVB_PLL if !DVB_FE_CUSTOMISE - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_STV0288 if !DVB_FE_CUSTOMISE - select DVB_STB6000 if !DVB_FE_CUSTOMISE - select DVB_CX24116 if !DVB_FE_CUSTOMISE - select DVB_SI21XX if !DVB_FE_CUSTOMISE - select DVB_TDA10023 if !DVB_FE_CUSTOMISE - select DVB_MT312 if !DVB_FE_CUSTOMISE - select DVB_ZL10039 if !DVB_FE_CUSTOMISE - select DVB_DS3000 if !DVB_FE_CUSTOMISE - select DVB_STB6100 if !DVB_FE_CUSTOMISE - select DVB_STV6110 if !DVB_FE_CUSTOMISE - select DVB_STV0900 if !DVB_FE_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT + select DVB_SI21XX if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MT312 if MEDIA_SUBDRV_AUTOSELECT + select DVB_ZL10039 if MEDIA_SUBDRV_AUTOSELECT + select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the DvbWorld, TeVii, Prof DVB-S/S2 USB2.0 receivers. @@ -285,8 +285,8 @@ config DVB_USB_CINERGY_T2 config DVB_USB_DTV5100 tristate "AME DTV-5100 USB2.0 DVB-T support" depends on DVB_USB - select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE + select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the AME DTV-5100 USB2.0 DVB-T receiver. @@ -299,15 +299,15 @@ config DVB_USB_FRIIO config DVB_USB_AZ6027 tristate "Azurewave DVB-S/S2 USB2.0 AZ6027 support" depends on DVB_USB - select DVB_STB0899 if !DVB_FE_CUSTOMISE - select DVB_STB6100 if !DVB_FE_CUSTOMISE + select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the AZ6027 device config DVB_USB_TECHNISAT_USB2 tristate "Technisat DVB-S/S2 USB2.0 support" depends on DVB_USB - select DVB_STV090x if !DVB_FE_CUSTOMISE - select DVB_STV6110x if !DVB_FE_CUSTOMISE + select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV6110x if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Technisat USB2 DVB-S/S2 device diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig index 928ef0d0429..7a5bd61bd3b 100644 --- a/drivers/media/usb/em28xx/Kconfig +++ b/drivers/media/usb/em28xx/Kconfig @@ -4,10 +4,10 @@ config VIDEO_EM28XX select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEOBUF_VMALLOC - select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_TVP5150 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_MT9V011 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_TVP5150 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_MSP3400 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_MT9V011 if MEDIA_SUBDRV_AUTOSELECT ---help--- This is a video4linux driver for Empia 28xx based TV cards. @@ -33,16 +33,16 @@ config VIDEO_EM28XX_ALSA config VIDEO_EM28XX_DVB tristate "DVB/ATSC Support for em28xx based TV cards" depends on VIDEO_EM28XX && DVB_CORE - select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select DVB_TDA10023 if !DVB_FE_CUSTOMISE - select DVB_S921 if !DVB_FE_CUSTOMISE - select DVB_DRXD if !DVB_FE_CUSTOMISE - select DVB_CXD2820R if !DVB_FE_CUSTOMISE - select DVB_DRXK if !DVB_FE_CUSTOMISE - select DVB_TDA18271C2DD if !DVB_FE_CUSTOMISE - select DVB_TDA10071 if !DVB_FE_CUSTOMISE - select DVB_A8293 if !DVB_FE_CUSTOMISE + select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT + select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT + select DVB_S921 if MEDIA_SUBDRV_AUTOSELECT + select DVB_DRXD if MEDIA_SUBDRV_AUTOSELECT + select DVB_CXD2820R if MEDIA_SUBDRV_AUTOSELECT + select DVB_DRXK if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10071 if MEDIA_SUBDRV_AUTOSELECT + select DVB_A8293 if MEDIA_SUBDRV_AUTOSELECT select VIDEOBUF_DVB ---help--- This adds support for DVB cards based on the diff --git a/drivers/media/usb/pvrusb2/Kconfig b/drivers/media/usb/pvrusb2/Kconfig index 25e412ecad2..32b11c15bb1 100644 --- a/drivers/media/usb/pvrusb2/Kconfig +++ b/drivers/media/usb/pvrusb2/Kconfig @@ -36,13 +36,13 @@ config VIDEO_PVRUSB2_DVB bool "pvrusb2 ATSC/DVB support (EXPERIMENTAL)" default y depends on VIDEO_PVRUSB2 && DVB_CORE && EXPERIMENTAL - select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_S5H1409 if !DVB_FE_CUSTOMISE - select DVB_S5H1411 if !DVB_FE_CUSTOMISE - select DVB_TDA10048 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE + select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1409 if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10048 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT ---help--- This option enables a DVB interface for the pvrusb2 driver. diff --git a/drivers/media/usb/ttusb-budget/Kconfig b/drivers/media/usb/ttusb-budget/Kconfig index 2663ae39b88..97bad7da689 100644 --- a/drivers/media/usb/ttusb-budget/Kconfig +++ b/drivers/media/usb/ttusb-budget/Kconfig @@ -1,13 +1,13 @@ config DVB_TTUSB_BUDGET tristate "Technotrend/Hauppauge Nova-USB devices" depends on DVB_CORE && USB && I2C && PCI - select DVB_CX22700 if !DVB_FE_CUSTOMISE - select DVB_TDA1004X if !DVB_FE_CUSTOMISE - select DVB_VES1820 if !DVB_FE_CUSTOMISE - select DVB_TDA8083 if !DVB_FE_CUSTOMISE - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_STV0297 if !DVB_FE_CUSTOMISE - select DVB_LNBP21 if !DVB_FE_CUSTOMISE + select DVB_CX22700 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT + select DVB_VES1820 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0297 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT help Support for external USB adapters designed by Technotrend and produced by Hauppauge, shipped under the brand name 'Nova-USB'. diff --git a/drivers/media/usb/usbvision/Kconfig b/drivers/media/usb/usbvision/Kconfig index fc24ef05b3f..6b6afc5d8f7 100644 --- a/drivers/media/usb/usbvision/Kconfig +++ b/drivers/media/usb/usbvision/Kconfig @@ -2,7 +2,7 @@ config VIDEO_USBVISION tristate "USB video devices based on Nogatech NT1003/1004/1005" depends on I2C && VIDEO_V4L2 select VIDEO_TUNER - select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT ---help--- There are more than 50 different USB video devices based on NT1003/1004/1005 USB Bridges. This driver enables using those -- cgit v1.2.3-70-g09d2 From e6eb28c2207b9397d0ab56e238865a4ee95b7ef9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 4 Sep 2012 10:26:45 -0300 Subject: [media] v4l2: make vidioc_s_fbuf const Write-only ioctls should have a const argument in the ioctl op. Do this conversion for vidioc_s_fbuf. Adding const for write-only ioctls was decided during the 2012 Media Workshop. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146/saa7146_video.c | 2 +- drivers/media/pci/bt8xx/bttv-driver.c | 2 +- drivers/media/pci/ivtv/ivtv-ioctl.c | 4 ++-- drivers/media/pci/saa7134/saa7134-video.c | 2 +- drivers/media/pci/zoran/zoran_driver.c | 2 +- drivers/media/platform/fsl-viu.c | 2 +- drivers/media/platform/omap/omap_vout.c | 2 +- include/media/v4l2-ioctl.h | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/media/pci/saa7134') diff --git a/drivers/media/common/saa7146/saa7146_video.c b/drivers/media/common/saa7146/saa7146_video.c index 6d14785d474..4143d61f79b 100644 --- a/drivers/media/common/saa7146/saa7146_video.c +++ b/drivers/media/common/saa7146/saa7146_video.c @@ -479,7 +479,7 @@ static int vidioc_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *f return 0; } -static int vidioc_s_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb) +static int vidioc_s_fbuf(struct file *file, void *fh, const struct v4l2_framebuffer *fb) { struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct saa7146_vv *vv = dev->vv_data; diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index b58ff87db77..26bf309c41c 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -2740,7 +2740,7 @@ static int bttv_overlay(struct file *file, void *f, unsigned int on) } static int bttv_s_fbuf(struct file *file, void *f, - struct v4l2_framebuffer *fb) + const struct v4l2_framebuffer *fb) { struct bttv_fh *fh = f; struct bttv *btv = fh->btv; diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c index 32a591062d0..d3b32c2d034 100644 --- a/drivers/media/pci/ivtv/ivtv-ioctl.c +++ b/drivers/media/pci/ivtv/ivtv-ioctl.c @@ -1427,7 +1427,7 @@ static int ivtv_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb) return 0; } -static int ivtv_s_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb) +static int ivtv_s_fbuf(struct file *file, void *fh, const struct v4l2_framebuffer *fb) { struct ivtv_open_id *id = fh2id(fh); struct ivtv *itv = id->itv; @@ -1444,7 +1444,7 @@ static int ivtv_s_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb) itv->osd_chroma_key_state = (fb->flags & V4L2_FBUF_FLAG_CHROMAKEY) != 0; ivtv_set_osd_alpha(itv); yi->track_osd = (fb->flags & V4L2_FBUF_FLAG_OVERLAY) != 0; - return ivtv_g_fbuf(file, fh, fb); + return 0; } static int ivtv_overlay(struct file *file, void *fh, unsigned int on) diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 6de10b1e725..bac4386c64b 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -2158,7 +2158,7 @@ static int saa7134_g_fbuf(struct file *file, void *f, } static int saa7134_s_fbuf(struct file *file, void *f, - struct v4l2_framebuffer *fb) + const struct v4l2_framebuffer *fb) { struct saa7134_fh *fh = f; struct saa7134_dev *dev = fh->dev; diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c index c6ccdeb6d8d..f91b551e6a5 100644 --- a/drivers/media/pci/zoran/zoran_driver.c +++ b/drivers/media/pci/zoran/zoran_driver.c @@ -1978,7 +1978,7 @@ static int zoran_g_fbuf(struct file *file, void *__fh, } static int zoran_s_fbuf(struct file *file, void *__fh, - struct v4l2_framebuffer *fb) + const struct v4l2_framebuffer *fb) { struct zoran_fh *fh = __fh; struct zoran *zr = fh->zr; diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index 20f981008fa..897250b8864 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -860,7 +860,7 @@ int vidioc_g_fbuf(struct file *file, void *priv, struct v4l2_framebuffer *arg) return 0; } -int vidioc_s_fbuf(struct file *file, void *priv, struct v4l2_framebuffer *arg) +int vidioc_s_fbuf(struct file *file, void *priv, const struct v4l2_framebuffer *arg) { struct viu_fh *fh = priv; struct viu_dev *dev = fh->dev; diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index 88cf9d95263..92845f83560 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -1744,7 +1744,7 @@ static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) } static int vidioc_s_fbuf(struct file *file, void *fh, - struct v4l2_framebuffer *a) + const struct v4l2_framebuffer *a) { int enable = 0; struct omap_overlay *ovl; diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index 0bc1444e50e..73ae24a41cb 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -120,7 +120,7 @@ struct v4l2_ioctl_ops { int (*vidioc_g_fbuf) (struct file *file, void *fh, struct v4l2_framebuffer *a); int (*vidioc_s_fbuf) (struct file *file, void *fh, - struct v4l2_framebuffer *a); + const struct v4l2_framebuffer *a); /* Stream on/off */ int (*vidioc_streamon) (struct file *file, void *fh, enum v4l2_buf_type i); -- cgit v1.2.3-70-g09d2 From 0e8025b9f6011a6bd69d01080d584bc95a89d02e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 4 Sep 2012 11:59:31 -0300 Subject: [media] v4l2: make vidioc_s_audio const Write-only ioctls should have a const argument in the ioctl op. Do this conversion for vidioc_s_audio. Adding const for write-only ioctls was decided during the 2012 Media Workshop. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/bt8xx/bttv-driver.c | 4 ++-- drivers/media/pci/cx18/cx18-ioctl.c | 2 +- drivers/media/pci/cx23885/cx23885-video.c | 2 +- drivers/media/pci/ivtv/ivtv-ioctl.c | 2 +- drivers/media/pci/saa7134/saa7134-video.c | 4 ++-- drivers/media/pci/saa7146/mxb.c | 2 +- drivers/media/pci/ttpci/av7110_v4l.c | 2 +- drivers/media/radio/radio-miropcm20.c | 2 +- drivers/media/radio/radio-sf16fmi.c | 2 +- drivers/media/radio/radio-tea5764.c | 2 +- drivers/media/radio/radio-timb.c | 2 +- drivers/media/radio/radio-wl1273.c | 2 +- drivers/media/radio/wl128x/fmdrv_v4l2.c | 2 +- drivers/media/usb/au0828/au0828-video.c | 2 +- drivers/media/usb/cx231xx/cx231xx-video.c | 4 ++-- drivers/media/usb/em28xx/em28xx-video.c | 4 ++-- drivers/media/usb/hdpvr/hdpvr-video.c | 2 +- drivers/media/usb/pvrusb2/pvrusb2-v4l2.c | 2 +- drivers/media/usb/tlg2300/pd-radio.c | 2 +- drivers/media/usb/tlg2300/pd-video.c | 2 +- drivers/media/usb/tm6000/tm6000-video.c | 2 +- drivers/media/usb/usbvision/usbvision-video.c | 2 +- include/media/v4l2-ioctl.h | 2 +- 23 files changed, 27 insertions(+), 27 deletions(-) (limited to 'drivers/media/pci/saa7134') diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index 26bf309c41c..31b28266746 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -3076,7 +3076,7 @@ static int bttv_g_audio(struct file *file, void *priv, struct v4l2_audio *a) return 0; } -static int bttv_s_audio(struct file *file, void *priv, struct v4l2_audio *a) +static int bttv_s_audio(struct file *file, void *priv, const struct v4l2_audio *a) { if (unlikely(a->index)) return -EINVAL; @@ -3480,7 +3480,7 @@ static int radio_s_tuner(struct file *file, void *priv, } static int radio_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) + const struct v4l2_audio *a) { if (unlikely(a->index)) return -EINVAL; diff --git a/drivers/media/pci/cx18/cx18-ioctl.c b/drivers/media/pci/cx18/cx18-ioctl.c index e9912db3b49..ff315446d4a 100644 --- a/drivers/media/pci/cx18/cx18-ioctl.c +++ b/drivers/media/pci/cx18/cx18-ioctl.c @@ -492,7 +492,7 @@ static int cx18_g_audio(struct file *file, void *fh, struct v4l2_audio *vin) return cx18_get_audio_input(cx, vin->index, vin); } -static int cx18_s_audio(struct file *file, void *fh, struct v4l2_audio *vout) +static int cx18_s_audio(struct file *file, void *fh, const struct v4l2_audio *vout) { struct cx18 *cx = fh2id(fh)->cx; diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index 22f8e7fbd66..8c4a9a5f9a5 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -1426,7 +1426,7 @@ static int vidioc_g_audinput(struct file *file, void *priv, } static int vidioc_s_audinput(struct file *file, void *priv, - struct v4l2_audio *i) + const struct v4l2_audio *i) { struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; if (i->index >= 2) diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c index 966abb40730..99e35dd3d83 100644 --- a/drivers/media/pci/ivtv/ivtv-ioctl.c +++ b/drivers/media/pci/ivtv/ivtv-ioctl.c @@ -784,7 +784,7 @@ static int ivtv_g_audio(struct file *file, void *fh, struct v4l2_audio *vin) return ivtv_get_audio_input(itv, vin->index, vin); } -static int ivtv_s_audio(struct file *file, void *fh, struct v4l2_audio *vout) +static int ivtv_s_audio(struct file *file, void *fh, const struct v4l2_audio *vout) { struct ivtv *itv = fh2id(fh)->itv; diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index bac4386c64b..135bfd8c28a 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -2089,7 +2089,7 @@ static int saa7134_g_audio(struct file *file, void *priv, struct v4l2_audio *a) return 0; } -static int saa7134_s_audio(struct file *file, void *priv, struct v4l2_audio *a) +static int saa7134_s_audio(struct file *file, void *priv, const struct v4l2_audio *a) { return 0; } @@ -2373,7 +2373,7 @@ static int radio_g_audio(struct file *file, void *priv, } static int radio_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) + const struct v4l2_audio *a) { return 0; } diff --git a/drivers/media/pci/saa7146/mxb.c b/drivers/media/pci/saa7146/mxb.c index b520a45cb3f..91369daad72 100644 --- a/drivers/media/pci/saa7146/mxb.c +++ b/drivers/media/pci/saa7146/mxb.c @@ -646,7 +646,7 @@ static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) return 0; } -static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a) +static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a) { struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct mxb *mxb = (struct mxb *)dev->ext_priv; diff --git a/drivers/media/pci/ttpci/av7110_v4l.c b/drivers/media/pci/ttpci/av7110_v4l.c index 1b2d15140a1..730e906ea91 100644 --- a/drivers/media/pci/ttpci/av7110_v4l.c +++ b/drivers/media/pci/ttpci/av7110_v4l.c @@ -526,7 +526,7 @@ static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) return 0; } -static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a) +static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a) { struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index 87c1ee13b05..11f76ed4c6f 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -197,7 +197,7 @@ static int vidioc_g_audio(struct file *file, void *priv, } static int vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) + const struct v4l2_audio *a) { return a->index ? -EINVAL : 0; } diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c index 8185d5fbfa8..227dcdb54df 100644 --- a/drivers/media/radio/radio-sf16fmi.c +++ b/drivers/media/radio/radio-sf16fmi.c @@ -239,7 +239,7 @@ static int vidioc_g_audio(struct file *file, void *priv, } static int vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) + const struct v4l2_audio *a) { return a->index ? -EINVAL : 0; } diff --git a/drivers/media/radio/radio-tea5764.c b/drivers/media/radio/radio-tea5764.c index 6b1fae32b48..efb05aa81d0 100644 --- a/drivers/media/radio/radio-tea5764.c +++ b/drivers/media/radio/radio-tea5764.c @@ -448,7 +448,7 @@ static int vidioc_g_audio(struct file *file, void *priv, } static int vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) + const struct v4l2_audio *a) { if (a->index != 0) return -EINVAL; diff --git a/drivers/media/radio/radio-timb.c b/drivers/media/radio/radio-timb.c index 09fc56052d3..5cf07779f4b 100644 --- a/drivers/media/radio/radio-timb.c +++ b/drivers/media/radio/radio-timb.c @@ -85,7 +85,7 @@ static int timbradio_vidioc_g_audio(struct file *file, void *priv, } static int timbradio_vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) + const struct v4l2_audio *a) { return a->index ? -EINVAL : 0; } diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c index 71968a61073..2d93354fdce 100644 --- a/drivers/media/radio/radio-wl1273.c +++ b/drivers/media/radio/radio-wl1273.c @@ -1479,7 +1479,7 @@ static int wl1273_fm_vidioc_g_audio(struct file *file, void *priv, } static int wl1273_fm_vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *audio) + const struct v4l2_audio *audio) { struct wl1273_device *radio = video_get_drvdata(video_devdata(file)); diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c index f816ea68e46..09585a99f02 100644 --- a/drivers/media/radio/wl128x/fmdrv_v4l2.c +++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c @@ -258,7 +258,7 @@ static int fm_v4l2_vidioc_g_audio(struct file *file, void *priv, } static int fm_v4l2_vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *audio) + const struct v4l2_audio *audio) { if (audio->index != 0) return -EINVAL; diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index fa0fa9ae91c..87058557057 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -1465,7 +1465,7 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) return 0; } -static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) +static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio *a) { struct au0828_fh *fh = priv; struct au0828_dev *dev = fh->dev; diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index 790b28d7f76..fedf7852a35 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -1253,7 +1253,7 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) return 0; } -static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) +static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio *a) { struct cx231xx_fh *fh = priv; struct cx231xx *dev = fh->dev; @@ -2096,7 +2096,7 @@ static int radio_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t) return 0; } -static int radio_s_audio(struct file *file, void *fh, struct v4l2_audio *a) +static int radio_s_audio(struct file *file, void *fh, const struct v4l2_audio *a) { return 0; } diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 78d6ebd712b..1e553d35738 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1352,7 +1352,7 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) return 0; } -static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) +static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio *a) { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; @@ -2087,7 +2087,7 @@ static int radio_s_tuner(struct file *file, void *priv, } static int radio_s_audio(struct file *file, void *fh, - struct v4l2_audio *a) + const struct v4l2_audio *a) { return 0; } diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c index 0e9e156bb2a..da6b7791222 100644 --- a/drivers/media/usb/hdpvr/hdpvr-video.c +++ b/drivers/media/usb/hdpvr/hdpvr-video.c @@ -677,7 +677,7 @@ static int vidioc_enumaudio(struct file *file, void *priv, } static int vidioc_s_audio(struct file *file, void *private_data, - struct v4l2_audio *audio) + const struct v4l2_audio *audio) { struct hdpvr_fh *fh = file->private_data; struct hdpvr_device *dev = fh->dev; diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c index f344aed32a9..7a445b0e725 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c @@ -333,7 +333,7 @@ static int pvr2_g_audio(struct file *file, void *priv, struct v4l2_audio *vin) return 0; } -static int pvr2_s_audio(struct file *file, void *priv, struct v4l2_audio *vout) +static int pvr2_s_audio(struct file *file, void *priv, const struct v4l2_audio *vout) { if (vout->index) return -EINVAL; diff --git a/drivers/media/usb/tlg2300/pd-radio.c b/drivers/media/usb/tlg2300/pd-radio.c index 4fad1dfb92c..25eeb166aa0 100644 --- a/drivers/media/usb/tlg2300/pd-radio.c +++ b/drivers/media/usb/tlg2300/pd-radio.c @@ -348,7 +348,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *vt) { return vt->index > 0 ? -EINVAL : 0; } -static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *va) +static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio *va) { return (va->index != 0) ? -EINVAL : 0; } diff --git a/drivers/media/usb/tlg2300/pd-video.c b/drivers/media/usb/tlg2300/pd-video.c index bfbf9e56b0a..1f448ac7a49 100644 --- a/drivers/media/usb/tlg2300/pd-video.c +++ b/drivers/media/usb/tlg2300/pd-video.c @@ -1029,7 +1029,7 @@ static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) return 0; } -static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a) +static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a) { return (0 == a->index) ? 0 : -EINVAL; } diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index 45ed59cb2d0..4342cd4f5c8 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -1401,7 +1401,7 @@ static int radio_g_audio(struct file *file, void *priv, } static int radio_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) + const struct v4l2_audio *a) { return 0; } diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index 8a4317979a4..f67018ed379 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -684,7 +684,7 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) } static int vidioc_s_audio(struct file *file, void *fh, - struct v4l2_audio *a) + const struct v4l2_audio *a) { if (a->index) return -EINVAL; diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index 3eef4de0ca3..babbe09527c 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -167,7 +167,7 @@ struct v4l2_ioctl_ops { int (*vidioc_g_audio) (struct file *file, void *fh, struct v4l2_audio *a); int (*vidioc_s_audio) (struct file *file, void *fh, - struct v4l2_audio *a); + const struct v4l2_audio *a); /* Audio out ioctls */ int (*vidioc_enumaudout) (struct file *file, void *fh, -- cgit v1.2.3-70-g09d2 From 4f996594ceaf6c3f9bc42b40c40b0f7f87b79c86 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 5 Sep 2012 05:10:48 -0300 Subject: [media] v4l2: make vidioc_s_crop const Write-only ioctls should have a const argument in the ioctl op. Do this conversion for vidioc_s_crop. Adding const for write-only ioctls was decided during the 2012 Media Workshop. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/soc_camera/mt9m001.c | 2 +- drivers/media/i2c/soc_camera/mt9m111.c | 2 +- drivers/media/i2c/soc_camera/mt9t031.c | 2 +- drivers/media/i2c/soc_camera/mt9t112.c | 4 +-- drivers/media/i2c/soc_camera/mt9v022.c | 2 +- drivers/media/i2c/soc_camera/ov5642.c | 20 +++++++------- drivers/media/i2c/soc_camera/ov6650.c | 32 +++++++++++----------- drivers/media/i2c/soc_camera/rj54n1cb0c.c | 4 +-- drivers/media/i2c/tvp5150.c | 2 +- drivers/media/pci/bt8xx/bttv-driver.c | 10 +++---- drivers/media/pci/cx18/cx18-ioctl.c | 2 +- drivers/media/pci/cx25821/cx25821-video.c | 2 +- drivers/media/pci/cx25821/cx25821-video.h | 2 +- drivers/media/pci/ivtv/ivtv-ioctl.c | 2 +- drivers/media/pci/saa7134/saa7134-video.c | 32 +++++++++++----------- drivers/media/pci/zoran/zoran_driver.c | 2 +- drivers/media/platform/davinci/vpbe_display.c | 2 +- drivers/media/platform/davinci/vpfe_capture.c | 2 +- drivers/media/platform/omap/omap_vout.c | 2 +- drivers/media/platform/s5p-fimc/fimc-m2m.c | 2 +- drivers/media/platform/s5p-g2d/g2d.c | 2 +- drivers/media/platform/sh_vou.c | 2 +- .../platform/soc_camera/sh_mobile_ceu_camera.c | 4 +-- drivers/media/platform/soc_camera/soc_camera.c | 6 ++-- drivers/media/platform/vino.c | 2 +- drivers/media/usb/pvrusb2/pvrusb2-v4l2.c | 2 +- drivers/staging/media/go7007/go7007-v4l2.c | 2 +- include/media/soc_camera.h | 4 +-- include/media/v4l2-ioctl.h | 2 +- include/media/v4l2-subdev.h | 2 +- 30 files changed, 79 insertions(+), 79 deletions(-) (limited to 'drivers/media/pci/saa7134') diff --git a/drivers/media/i2c/soc_camera/mt9m001.c b/drivers/media/i2c/soc_camera/mt9m001.c index d85be41ffa1..19f8a07764f 100644 --- a/drivers/media/i2c/soc_camera/mt9m001.c +++ b/drivers/media/i2c/soc_camera/mt9m001.c @@ -171,7 +171,7 @@ static int mt9m001_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int mt9m001_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9m001 *mt9m001 = to_mt9m001(client); diff --git a/drivers/media/i2c/soc_camera/mt9m111.c b/drivers/media/i2c/soc_camera/mt9m111.c index 938c5c390ee..62fd94af599 100644 --- a/drivers/media/i2c/soc_camera/mt9m111.c +++ b/drivers/media/i2c/soc_camera/mt9m111.c @@ -383,7 +383,7 @@ static int mt9m111_reset(struct mt9m111 *mt9m111) return ret; } -static int mt9m111_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int mt9m111_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) { struct v4l2_rect rect = a->c; struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); diff --git a/drivers/media/i2c/soc_camera/mt9t031.c b/drivers/media/i2c/soc_camera/mt9t031.c index d74607adc58..40800b10a08 100644 --- a/drivers/media/i2c/soc_camera/mt9t031.c +++ b/drivers/media/i2c/soc_camera/mt9t031.c @@ -294,7 +294,7 @@ static int mt9t031_set_params(struct i2c_client *client, return ret < 0 ? ret : 0; } -static int mt9t031_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int mt9t031_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) { struct v4l2_rect rect = a->c; struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/soc_camera/mt9t112.c b/drivers/media/i2c/soc_camera/mt9t112.c index 9ba428ede51..de7cd836b0a 100644 --- a/drivers/media/i2c/soc_camera/mt9t112.c +++ b/drivers/media/i2c/soc_camera/mt9t112.c @@ -907,11 +907,11 @@ static int mt9t112_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) return 0; } -static int mt9t112_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int mt9t112_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9t112_priv *priv = to_mt9t112(client); - struct v4l2_rect *rect = &a->c; + const struct v4l2_rect *rect = &a->c; return mt9t112_set_params(priv, rect, priv->format->code); } diff --git a/drivers/media/i2c/soc_camera/mt9v022.c b/drivers/media/i2c/soc_camera/mt9v022.c index 350d0d85444..13057b966ee 100644 --- a/drivers/media/i2c/soc_camera/mt9v022.c +++ b/drivers/media/i2c/soc_camera/mt9v022.c @@ -237,7 +237,7 @@ static int mt9v022_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static int mt9v022_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int mt9v022_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9v022 *mt9v022 = to_mt9v022(client); diff --git a/drivers/media/i2c/soc_camera/ov5642.c b/drivers/media/i2c/soc_camera/ov5642.c index d886c0b9ce4..8577e0cfb7f 100644 --- a/drivers/media/i2c/soc_camera/ov5642.c +++ b/drivers/media/i2c/soc_camera/ov5642.c @@ -865,24 +865,24 @@ static int ov5642_g_chip_ident(struct v4l2_subdev *sd, return 0; } -static int ov5642_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int ov5642_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov5642 *priv = to_ov5642(client); - struct v4l2_rect *rect = &a->c; + struct v4l2_rect rect = a->c; int ret; - v4l_bound_align_image(&rect->width, 48, OV5642_MAX_WIDTH, 1, - &rect->height, 32, OV5642_MAX_HEIGHT, 1, 0); + v4l_bound_align_image(&rect.width, 48, OV5642_MAX_WIDTH, 1, + &rect.height, 32, OV5642_MAX_HEIGHT, 1, 0); - priv->crop_rect.width = rect->width; - priv->crop_rect.height = rect->height; - priv->total_width = rect->width + BLANKING_EXTRA_WIDTH; - priv->total_height = max_t(int, rect->height + + priv->crop_rect.width = rect.width; + priv->crop_rect.height = rect.height; + priv->total_width = rect.width + BLANKING_EXTRA_WIDTH; + priv->total_height = max_t(int, rect.height + BLANKING_EXTRA_HEIGHT, BLANKING_MIN_HEIGHT); - priv->crop_rect.width = rect->width; - priv->crop_rect.height = rect->height; + priv->crop_rect.width = rect.width; + priv->crop_rect.height = rect.height; ret = ov5642_write_array(client, ov5642_default_regs_init); if (!ret) diff --git a/drivers/media/i2c/soc_camera/ov6650.c b/drivers/media/i2c/soc_camera/ov6650.c index 65b031f333b..e87feb0881e 100644 --- a/drivers/media/i2c/soc_camera/ov6650.c +++ b/drivers/media/i2c/soc_camera/ov6650.c @@ -451,42 +451,42 @@ static int ov6650_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) return 0; } -static int ov6650_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int ov6650_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov6650 *priv = to_ov6650(client); - struct v4l2_rect *rect = &a->c; + struct v4l2_rect rect = a->c; int ret; if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - rect->left = ALIGN(rect->left, 2); - rect->width = ALIGN(rect->width, 2); - rect->top = ALIGN(rect->top, 2); - rect->height = ALIGN(rect->height, 2); - soc_camera_limit_side(&rect->left, &rect->width, + rect.left = ALIGN(rect.left, 2); + rect.width = ALIGN(rect.width, 2); + rect.top = ALIGN(rect.top, 2); + rect.height = ALIGN(rect.height, 2); + soc_camera_limit_side(&rect.left, &rect.width, DEF_HSTRT << 1, 2, W_CIF); - soc_camera_limit_side(&rect->top, &rect->height, + soc_camera_limit_side(&rect.top, &rect.height, DEF_VSTRT << 1, 2, H_CIF); - ret = ov6650_reg_write(client, REG_HSTRT, rect->left >> 1); + ret = ov6650_reg_write(client, REG_HSTRT, rect.left >> 1); if (!ret) { - priv->rect.left = rect->left; + priv->rect.left = rect.left; ret = ov6650_reg_write(client, REG_HSTOP, - (rect->left + rect->width) >> 1); + (rect.left + rect.width) >> 1); } if (!ret) { - priv->rect.width = rect->width; - ret = ov6650_reg_write(client, REG_VSTRT, rect->top >> 1); + priv->rect.width = rect.width; + ret = ov6650_reg_write(client, REG_VSTRT, rect.top >> 1); } if (!ret) { - priv->rect.top = rect->top; + priv->rect.top = rect.top; ret = ov6650_reg_write(client, REG_VSTOP, - (rect->top + rect->height) >> 1); + (rect.top + rect.height) >> 1); } if (!ret) - priv->rect.height = rect->height; + priv->rect.height = rect.height; return ret; } diff --git a/drivers/media/i2c/soc_camera/rj54n1cb0c.c b/drivers/media/i2c/soc_camera/rj54n1cb0c.c index 32226c9024f..02f0400051d 100644 --- a/drivers/media/i2c/soc_camera/rj54n1cb0c.c +++ b/drivers/media/i2c/soc_camera/rj54n1cb0c.c @@ -536,11 +536,11 @@ static int rj54n1_commit(struct i2c_client *client) static int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h, s32 *out_w, s32 *out_h); -static int rj54n1_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int rj54n1_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct rj54n1 *rj54n1 = to_rj54n1(client); - struct v4l2_rect *rect = &a->c; + const struct v4l2_rect *rect = &a->c; int dummy = 0, output_w, output_h, input_w = rect->width, input_h = rect->height; int ret; diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index a751b6c146f..b5b1792479d 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -865,7 +865,7 @@ static int tvp5150_mbus_fmt(struct v4l2_subdev *sd, return 0; } -static int tvp5150_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int tvp5150_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) { struct v4l2_rect rect = a->c; struct tvp5150 *decoder = to_tvp5150(sd); diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index 31b28266746..16f5ca23698 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -2986,7 +2986,7 @@ static int bttv_g_crop(struct file *file, void *f, struct v4l2_crop *crop) return 0; } -static int bttv_s_crop(struct file *file, void *f, struct v4l2_crop *crop) +static int bttv_s_crop(struct file *file, void *f, const struct v4l2_crop *crop) { struct bttv_fh *fh = f; struct bttv *btv = fh->btv; @@ -3028,17 +3028,17 @@ static int bttv_s_crop(struct file *file, void *f, struct v4l2_crop *crop) } /* Min. scaled size 48 x 32. */ - c.rect.left = clamp(crop->c.left, b_left, b_right - 48); + c.rect.left = clamp_t(s32, crop->c.left, b_left, b_right - 48); c.rect.left = min(c.rect.left, (__s32) MAX_HDELAY); - c.rect.width = clamp(crop->c.width, + c.rect.width = clamp_t(s32, crop->c.width, 48, b_right - c.rect.left); - c.rect.top = clamp(crop->c.top, b_top, b_bottom - 32); + c.rect.top = clamp_t(s32, crop->c.top, b_top, b_bottom - 32); /* Top and height must be a multiple of two. */ c.rect.top = (c.rect.top + 1) & ~1; - c.rect.height = clamp(crop->c.height, + c.rect.height = clamp_t(s32, crop->c.height, 32, b_bottom - c.rect.top); c.rect.height = (c.rect.height + 1) & ~1; diff --git a/drivers/media/pci/cx18/cx18-ioctl.c b/drivers/media/pci/cx18/cx18-ioctl.c index ff315446d4a..bb5073f72c4 100644 --- a/drivers/media/pci/cx18/cx18-ioctl.c +++ b/drivers/media/pci/cx18/cx18-ioctl.c @@ -527,7 +527,7 @@ static int cx18_cropcap(struct file *file, void *fh, return 0; } -static int cx18_s_crop(struct file *file, void *fh, struct v4l2_crop *crop) +static int cx18_s_crop(struct file *file, void *fh, const struct v4l2_crop *crop) { struct cx18_open_id *id = fh2id(fh); struct cx18 *cx = id->cx; diff --git a/drivers/media/pci/cx25821/cx25821-video.c b/drivers/media/pci/cx25821/cx25821-video.c index b38d4379cc3..0a80245165d 100644 --- a/drivers/media/pci/cx25821/cx25821-video.c +++ b/drivers/media/pci/cx25821/cx25821-video.c @@ -1610,7 +1610,7 @@ int cx25821_vidioc_cropcap(struct file *file, void *priv, return 0; } -int cx25821_vidioc_s_crop(struct file *file, void *priv, struct v4l2_crop *crop) +int cx25821_vidioc_s_crop(struct file *file, void *priv, const struct v4l2_crop *crop) { struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; struct cx25821_fh *fh = priv; diff --git a/drivers/media/pci/cx25821/cx25821-video.h b/drivers/media/pci/cx25821/cx25821-video.h index 9652a5e35ba..c265e35b37c 100644 --- a/drivers/media/pci/cx25821/cx25821-video.h +++ b/drivers/media/pci/cx25821/cx25821-video.h @@ -177,7 +177,7 @@ extern int cx25821_set_control(struct cx25821_dev *dev, extern int cx25821_vidioc_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap); extern int cx25821_vidioc_s_crop(struct file *file, void *priv, - struct v4l2_crop *crop); + const struct v4l2_crop *crop); extern int cx25821_vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop); diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c index d5cbb617775..ed6dcc7e61b 100644 --- a/drivers/media/pci/ivtv/ivtv-ioctl.c +++ b/drivers/media/pci/ivtv/ivtv-ioctl.c @@ -874,7 +874,7 @@ static int ivtv_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropca return 0; } -static int ivtv_s_crop(struct file *file, void *fh, struct v4l2_crop *crop) +static int ivtv_s_crop(struct file *file, void *fh, const struct v4l2_crop *crop) { struct ivtv_open_id *id = fh2id(fh); struct ivtv *itv = id->itv; diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 135bfd8c28a..22f8758d047 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -1953,11 +1953,12 @@ static int saa7134_g_crop(struct file *file, void *f, struct v4l2_crop *crop) return 0; } -static int saa7134_s_crop(struct file *file, void *f, struct v4l2_crop *crop) +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) @@ -1972,21 +1973,20 @@ static int saa7134_s_crop(struct file *file, void *f, struct v4l2_crop *crop) if (res_locked(fh->dev, RESOURCE_VIDEO)) return -EBUSY; - if (crop->c.top < b->top) - crop->c.top = b->top; - if (crop->c.top > b->top + b->height) - crop->c.top = b->top + b->height; - if (crop->c.height > b->top - crop->c.top + b->height) - crop->c.height = b->top - crop->c.top + b->height; - - if (crop->c.left < b->left) - crop->c.left = b->left; - if (crop->c.left > b->left + b->width) - crop->c.left = b->left + b->width; - if (crop->c.width > b->left - crop->c.left + b->width) - crop->c.width = b->left - crop->c.left + b->width; - - dev->crop_current = crop->c; + *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; } diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c index 9ecd7d711f2..53f12c7466b 100644 --- a/drivers/media/pci/zoran/zoran_driver.c +++ b/drivers/media/pci/zoran/zoran_driver.c @@ -2598,7 +2598,7 @@ gcrop_unlock_and_return: return res; } -static int zoran_s_crop(struct file *file, void *__fh, struct v4l2_crop *crop) +static int zoran_s_crop(struct file *file, void *__fh, const struct v4l2_crop *crop) { struct zoran_fh *fh = __fh; struct zoran *zr = fh->zr; diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index 9a05c817462..e712d6734ac 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -629,7 +629,7 @@ static int vpbe_display_querycap(struct file *file, void *priv, } static int vpbe_display_s_crop(struct file *file, void *priv, - struct v4l2_crop *crop) + const struct v4l2_crop *crop) { struct vpbe_fh *fh = file->private_data; struct vpbe_layer *layer = fh->layer; diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c index f99198cebd3..48052cbffc2 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/davinci/vpfe_capture.c @@ -1666,7 +1666,7 @@ static int vpfe_g_crop(struct file *file, void *priv, } static int vpfe_s_crop(struct file *file, void *priv, - struct v4l2_crop *crop) + const struct v4l2_crop *crop) { struct vpfe_device *vpfe_dev = video_drvdata(file); int ret = 0; diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index 92845f83560..36c3be85649 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -1291,7 +1291,7 @@ static int vidioc_g_crop(struct file *file, void *fh, struct v4l2_crop *crop) return 0; } -static int vidioc_s_crop(struct file *file, void *fh, struct v4l2_crop *crop) +static int vidioc_s_crop(struct file *file, void *fh, const struct v4l2_crop *crop) { int ret = -EINVAL; struct omap_vout_device *vout = fh; diff --git a/drivers/media/platform/s5p-fimc/fimc-m2m.c b/drivers/media/platform/s5p-fimc/fimc-m2m.c index ab4c15acdc4..c67e53bfa43 100644 --- a/drivers/media/platform/s5p-fimc/fimc-m2m.c +++ b/drivers/media/platform/s5p-fimc/fimc-m2m.c @@ -551,7 +551,7 @@ static int fimc_m2m_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) return 0; } -static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) +static int fimc_m2m_s_crop(struct file *file, void *fh, const struct v4l2_crop *cr) { struct fimc_ctx *ctx = fh_to_ctx(fh); struct fimc_dev *fimc = ctx->fimc_dev; diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 30195ef5a80..69c9f22ee52 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -526,7 +526,7 @@ static int vidioc_try_crop(struct file *file, void *prv, struct v4l2_crop *cr) return 0; } -static int vidioc_s_crop(struct file *file, void *prv, struct v4l2_crop *cr) +static int vidioc_s_crop(struct file *file, void *prv, const struct v4l2_crop *cr) { struct g2d_ctx *ctx = prv; struct g2d_frame *f; diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index 9f62fd89ab5..00cd52c61fe 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -933,7 +933,7 @@ static int sh_vou_g_crop(struct file *file, void *fh, struct v4l2_crop *a) } /* Assume a dull encoder, do all the work ourselves. */ -static int sh_vou_s_crop(struct file *file, void *fh, struct v4l2_crop *a) +static int sh_vou_s_crop(struct file *file, void *fh, const struct v4l2_crop *a) { struct video_device *vdev = video_devdata(file); struct sh_vou_device *vou_dev = video_get_drvdata(vdev); diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index 0baaf94db7e..0a24253dcda 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -1263,7 +1263,7 @@ static void update_subrect(struct sh_mobile_ceu_cam *cam) * 3. if (2) failed, try to request the maximum image */ static int client_s_crop(struct soc_camera_device *icd, struct v4l2_crop *crop, - struct v4l2_crop *cam_crop) + const struct v4l2_crop *cam_crop) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct v4l2_rect *rect = &crop->c, *cam_rect = &cam_crop->c; @@ -1517,7 +1517,7 @@ static int client_scale(struct soc_camera_device *icd, * scaling and cropping algorithms and for the meaning of referenced here steps. */ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, - struct v4l2_crop *a) + const struct v4l2_crop *a) { struct v4l2_rect *rect = &a->c; struct device *dev = icd->parent; diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 10b57f8e7ec..f6b1c1f8776 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -888,11 +888,11 @@ static int soc_camera_g_crop(struct file *file, void *fh, * retrieve it. */ static int soc_camera_s_crop(struct file *file, void *fh, - struct v4l2_crop *a) + const struct v4l2_crop *a) { struct soc_camera_device *icd = file->private_data; struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct v4l2_rect *rect = &a->c; + const struct v4l2_rect *rect = &a->c; struct v4l2_crop current_crop; int ret; @@ -1289,7 +1289,7 @@ static int default_g_crop(struct soc_camera_device *icd, struct v4l2_crop *a) return v4l2_subdev_call(sd, video, g_crop, a); } -static int default_s_crop(struct soc_camera_device *icd, struct v4l2_crop *a) +static int default_s_crop(struct soc_camera_device *icd, const struct v4l2_crop *a) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); return v4l2_subdev_call(sd, video, s_crop, a); diff --git a/drivers/media/platform/vino.c b/drivers/media/platform/vino.c index aae1720b2f2..790d96cffee 100644 --- a/drivers/media/platform/vino.c +++ b/drivers/media/platform/vino.c @@ -3284,7 +3284,7 @@ static int vino_g_crop(struct file *file, void *__fh, } static int vino_s_crop(struct file *file, void *__fh, - struct v4l2_crop *c) + const struct v4l2_crop *c) { struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c index 7a445b0e725..db249cad3cd 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c @@ -760,7 +760,7 @@ static int pvr2_g_crop(struct file *file, void *priv, struct v4l2_crop *crop) return 0; } -static int pvr2_s_crop(struct file *file, void *priv, struct v4l2_crop *crop) +static int pvr2_s_crop(struct file *file, void *priv, const struct v4l2_crop *crop) { struct pvr2_v4l2_fh *fh = file->private_data; struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c index f1dff3d0995..980371b0274 100644 --- a/drivers/staging/media/go7007/go7007-v4l2.c +++ b/drivers/staging/media/go7007/go7007-v4l2.c @@ -1372,7 +1372,7 @@ static int vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop) /* FIXME: vidioc_s_crop is not really implemented!!! */ -static int vidioc_s_crop(struct file *file, void *priv, struct v4l2_crop *crop) +static int vidioc_s_crop(struct file *file, void *priv, const struct v4l2_crop *crop) { if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 435e7b8ad1c..6442edc2a15 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -85,14 +85,14 @@ struct soc_camera_host_ops { void (*put_formats)(struct soc_camera_device *); int (*cropcap)(struct soc_camera_device *, struct v4l2_cropcap *); int (*get_crop)(struct soc_camera_device *, struct v4l2_crop *); - int (*set_crop)(struct soc_camera_device *, struct v4l2_crop *); + int (*set_crop)(struct soc_camera_device *, const struct v4l2_crop *); int (*get_selection)(struct soc_camera_device *, struct v4l2_selection *); int (*set_selection)(struct soc_camera_device *, struct v4l2_selection *); /* * The difference to .set_crop() is, that .set_livecrop is not allowed * to change the output sizes */ - int (*set_livecrop)(struct soc_camera_device *, struct v4l2_crop *); + int (*set_livecrop)(struct soc_camera_device *, const struct v4l2_crop *); int (*set_fmt)(struct soc_camera_device *, struct v4l2_format *); int (*try_fmt)(struct soc_camera_device *, struct v4l2_format *); void (*init_videobuf)(struct videobuf_queue *, diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index fbeb00e2c10..e48b571ca37 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -186,7 +186,7 @@ struct v4l2_ioctl_ops { int (*vidioc_g_crop) (struct file *file, void *fh, struct v4l2_crop *a); int (*vidioc_s_crop) (struct file *file, void *fh, - struct v4l2_crop *a); + const struct v4l2_crop *a); int (*vidioc_g_selection) (struct file *file, void *fh, struct v4l2_selection *s); int (*vidioc_s_selection) (struct file *file, void *fh, diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index e698f2cead4..2ecd7377153 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -286,7 +286,7 @@ struct v4l2_subdev_video_ops { int (*s_stream)(struct v4l2_subdev *sd, int enable); int (*cropcap)(struct v4l2_subdev *sd, struct v4l2_cropcap *cc); int (*g_crop)(struct v4l2_subdev *sd, struct v4l2_crop *crop); - int (*s_crop)(struct v4l2_subdev *sd, struct v4l2_crop *crop); + int (*s_crop)(struct v4l2_subdev *sd, const struct v4l2_crop *crop); int (*g_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param); int (*s_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param); int (*g_frame_interval)(struct v4l2_subdev *sd, -- cgit v1.2.3-70-g09d2 From c2c1b4156a447f113ef4d167decce29399c2667c Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Fri, 28 Sep 2012 05:37:22 -0300 Subject: [media] drivers/media: Remove unnecessary semicolon A simplified version of the semantic patch that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @r1@ statement S; position p,p1; @@ S@p1;@p @script:python r2@ p << r1.p; p1 << r1.p1; @@ if p[0].line != p1[0].line_end: cocci.include_match(False) @@ position r1.p; @@ -;@p // [mchehab@redhat.com: some hunks got bitroted; applied only the ones that succeeds] Signed-off-by: Peter Senna Tschudin [crope@iki.fi: For my drivers a8293, af9013, af9015, af9035] Acked-by: Antti Palosaari Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb_frontend.c | 2 +- drivers/media/dvb-frontends/a8293.c | 2 +- drivers/media/dvb-frontends/af9013.c | 6 +++--- drivers/media/dvb-frontends/bcm3510.c | 2 +- drivers/media/dvb-frontends/cx24110.c | 6 +++--- drivers/media/dvb-frontends/drxd_hard.c | 2 +- drivers/media/dvb-frontends/isl6405.c | 2 +- drivers/media/dvb-frontends/isl6421.c | 2 +- drivers/media/dvb-frontends/lnbp21.c | 4 ++-- drivers/media/dvb-frontends/lnbp22.c | 2 +- drivers/media/dvb-frontends/si21xx.c | 4 ++-- drivers/media/dvb-frontends/sp887x.c | 6 +++--- drivers/media/dvb-frontends/stv0299.c | 6 +++--- drivers/media/dvb-frontends/stv0900_core.c | 4 ++-- drivers/media/dvb-frontends/tda8083.c | 4 ++-- drivers/media/i2c/cx25840/cx25840-core.c | 2 +- drivers/media/pci/bt8xx/dst_ca.c | 2 +- drivers/media/pci/cx23885/altera-ci.c | 4 ++-- drivers/media/pci/cx23885/cimax2.c | 2 +- drivers/media/pci/cx88/cx88-dvb.c | 2 +- drivers/media/pci/cx88/cx88-mpeg.c | 2 +- drivers/media/pci/cx88/cx88-tvaudio.c | 4 ++-- drivers/media/pci/cx88/cx88-video.c | 2 +- drivers/media/pci/saa7134/saa7134-video.c | 2 +- drivers/media/platform/exynos-gsc/gsc-regs.c | 4 ++-- drivers/media/radio/si470x/radio-si470x-i2c.c | 2 +- drivers/media/radio/si470x/radio-si470x-usb.c | 2 +- drivers/media/usb/dvb-usb-v2/af9015.c | 4 ++-- drivers/media/usb/dvb-usb-v2/af9035.c | 2 +- 29 files changed, 45 insertions(+), 45 deletions(-) (limited to 'drivers/media/pci/saa7134') diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index 8f58f241c10..41af996c413 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -2309,7 +2309,7 @@ static int dvb_frontend_ioctl_legacy(struct file *file, fepriv->tune_mode_flags = (unsigned long) parg; err = 0; break; - }; + } return err; } diff --git a/drivers/media/dvb-frontends/a8293.c b/drivers/media/dvb-frontends/a8293.c index cff44a389b4..74fbb5d58be 100644 --- a/drivers/media/dvb-frontends/a8293.c +++ b/drivers/media/dvb-frontends/a8293.c @@ -90,7 +90,7 @@ static int a8293_set_voltage(struct dvb_frontend *fe, default: ret = -EINVAL; goto err; - }; + } ret = a8293_wr(priv, &priv->reg[0], 1); if (ret) diff --git a/drivers/media/dvb-frontends/af9013.c b/drivers/media/dvb-frontends/af9013.c index e9f04a36577..a204f282882 100644 --- a/drivers/media/dvb-frontends/af9013.c +++ b/drivers/media/dvb-frontends/af9013.c @@ -241,7 +241,7 @@ static int af9013_set_gpio(struct af9013_state *state, u8 gpio, u8 gpioval) KBUILD_MODNAME, gpio); ret = -EINVAL; goto err; - }; + } switch (gpio) { case 0: @@ -253,7 +253,7 @@ static int af9013_set_gpio(struct af9013_state *state, u8 gpio, u8 gpioval) default: pos = 4; break; - }; + } ret = af9013_wr_reg_bits(state, addr, pos, 4, gpioval); if (ret) @@ -726,7 +726,7 @@ static int af9013_set_frontend(struct dvb_frontend *fe) default: dev_dbg(&state->i2c->dev, "%s: invalid hierarchy\n", __func__); auto_mode = 1; - }; + } switch (c->modulation) { case QAM_AUTO: diff --git a/drivers/media/dvb-frontends/bcm3510.c b/drivers/media/dvb-frontends/bcm3510.c index 033cd7ad3ca..1b77909c0c7 100644 --- a/drivers/media/dvb-frontends/bcm3510.c +++ b/drivers/media/dvb-frontends/bcm3510.c @@ -527,7 +527,7 @@ static int bcm3510_set_frontend(struct dvb_frontend *fe) cmd.ACQUIRE1.IF_FREQ = 0x0; default: return -EINVAL; - }; + } cmd.ACQUIRE0.OFFSET = 0; cmd.ACQUIRE0.NTSCSWEEP = 1; cmd.ACQUIRE0.FA = 1; diff --git a/drivers/media/dvb-frontends/cx24110.c b/drivers/media/dvb-frontends/cx24110.c index 3180f5b2a6a..0cd6927e654 100644 --- a/drivers/media/dvb-frontends/cx24110.c +++ b/drivers/media/dvb-frontends/cx24110.c @@ -218,7 +218,7 @@ static int cx24110_set_fec (struct cx24110_state* state, fe_code_rate_t fec) } else return -EOPNOTSUPP; /* fixme (low): which is the correct return code? */ - }; + } return 0; } @@ -275,7 +275,7 @@ static int cx24110_set_symbolrate (struct cx24110_state* state, u32 srate) cx24110_writereg(state,0x07,tmp|0x3); cx24110_writereg(state,0x06,0x78); fclk=90999000UL; - }; + } dprintk("cx24110 debug: fclk %d Hz\n",fclk); /* we need to divide two integers with approx. 27 bits in 32 bit arithmetic giving a 25 bit result */ @@ -362,7 +362,7 @@ static int cx24110_initfe(struct dvb_frontend* fe) for(i = 0; i < ARRAY_SIZE(cx24110_regdata); i++) { cx24110_writereg(state, cx24110_regdata[i].reg, cx24110_regdata[i].data); - }; + } return 0; } diff --git a/drivers/media/dvb-frontends/drxd_hard.c b/drivers/media/dvb-frontends/drxd_hard.c index f380eb43e9d..6d9853750d2 100644 --- a/drivers/media/dvb-frontends/drxd_hard.c +++ b/drivers/media/dvb-frontends/drxd_hard.c @@ -991,7 +991,7 @@ static int HI_Command(struct drxd_state *state, u16 cmd, u16 * pResult) if (nrRetries > DRXD_MAX_RETRIES) { status = -1; break; - }; + } status = Read16(state, HI_RA_RAM_SRV_CMD__A, &waitCmd, 0); } while (waitCmd != 0); diff --git a/drivers/media/dvb-frontends/isl6405.c b/drivers/media/dvb-frontends/isl6405.c index 33d33f4d886..0c642a5bf82 100644 --- a/drivers/media/dvb-frontends/isl6405.c +++ b/drivers/media/dvb-frontends/isl6405.c @@ -77,7 +77,7 @@ static int isl6405_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage break; default: return -EINVAL; - }; + } } isl6405->config |= isl6405->override_or; isl6405->config &= isl6405->override_and; diff --git a/drivers/media/dvb-frontends/isl6421.c b/drivers/media/dvb-frontends/isl6421.c index 684c8ec166c..0cb3f0f74c9 100644 --- a/drivers/media/dvb-frontends/isl6421.c +++ b/drivers/media/dvb-frontends/isl6421.c @@ -63,7 +63,7 @@ static int isl6421_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage break; default: return -EINVAL; - }; + } isl6421->config |= isl6421->override_or; isl6421->config &= isl6421->override_and; diff --git a/drivers/media/dvb-frontends/lnbp21.c b/drivers/media/dvb-frontends/lnbp21.c index 13437259eea..f3ba7b5faa2 100644 --- a/drivers/media/dvb-frontends/lnbp21.c +++ b/drivers/media/dvb-frontends/lnbp21.c @@ -65,7 +65,7 @@ static int lnbp21_set_voltage(struct dvb_frontend *fe, break; default: return -EINVAL; - }; + } lnbp21->config |= lnbp21->override_or; lnbp21->config &= lnbp21->override_and; @@ -108,7 +108,7 @@ static int lnbp21_set_tone(struct dvb_frontend *fe, break; default: return -EINVAL; - }; + } lnbp21->config |= lnbp21->override_or; lnbp21->config &= lnbp21->override_and; diff --git a/drivers/media/dvb-frontends/lnbp22.c b/drivers/media/dvb-frontends/lnbp22.c index 84ad0390a4a..c463da7f6dc 100644 --- a/drivers/media/dvb-frontends/lnbp22.c +++ b/drivers/media/dvb-frontends/lnbp22.c @@ -73,7 +73,7 @@ static int lnbp22_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) break; default: return -EINVAL; - }; + } dprintk(1, "%s: 0x%02x)\n", __func__, lnbp22->config[3]); return (i2c_transfer(lnbp22->i2c, &msg, 1) == 1) ? 0 : -EIO; diff --git a/drivers/media/dvb-frontends/si21xx.c b/drivers/media/dvb-frontends/si21xx.c index a68a64800df..73b47cc6a13 100644 --- a/drivers/media/dvb-frontends/si21xx.c +++ b/drivers/media/dvb-frontends/si21xx.c @@ -343,7 +343,7 @@ static int si21xx_wait_diseqc_idle(struct si21xx_state *state, int timeout) return -ETIMEDOUT; } msleep(10); - }; + } return 0; } @@ -472,7 +472,7 @@ static int si21xx_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt) break; default: return -EINVAL; - }; + } } static int si21xx_init(struct dvb_frontend *fe) diff --git a/drivers/media/dvb-frontends/sp887x.c b/drivers/media/dvb-frontends/sp887x.c index f4096ccb226..1bb81b5ae6e 100644 --- a/drivers/media/dvb-frontends/sp887x.c +++ b/drivers/media/dvb-frontends/sp887x.c @@ -229,7 +229,7 @@ static int configure_reg0xc05(struct dtv_frontend_properties *p, u16 *reg0xc05) break; default: return -EINVAL; - }; + } switch (p->hierarchy) { case HIERARCHY_NONE: @@ -248,7 +248,7 @@ static int configure_reg0xc05(struct dtv_frontend_properties *p, u16 *reg0xc05) break; default: return -EINVAL; - }; + } switch (p->code_rate_HP) { case FEC_1_2: @@ -270,7 +270,7 @@ static int configure_reg0xc05(struct dtv_frontend_properties *p, u16 *reg0xc05) break; default: return -EINVAL; - }; + } if (known_parameters) *reg0xc05 |= (2 << 1); /* use specified parameters */ diff --git a/drivers/media/dvb-frontends/stv0299.c b/drivers/media/dvb-frontends/stv0299.c index 057b5f8effc..92a6075cd82 100644 --- a/drivers/media/dvb-frontends/stv0299.c +++ b/drivers/media/dvb-frontends/stv0299.c @@ -199,7 +199,7 @@ static int stv0299_wait_diseqc_fifo (struct stv0299_state* state, int timeout) return -ETIMEDOUT; } msleep(10); - }; + } return 0; } @@ -216,7 +216,7 @@ static int stv0299_wait_diseqc_idle (struct stv0299_state* state, int timeout) return -ETIMEDOUT; } msleep(10); - }; + } return 0; } @@ -387,7 +387,7 @@ static int stv0299_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltag break; default: return -EINVAL; - }; + } if (state->config->op0_off) reg0x0c &= ~0x10; diff --git a/drivers/media/dvb-frontends/stv0900_core.c b/drivers/media/dvb-frontends/stv0900_core.c index 7f1badaf0d0..262dfa503c2 100644 --- a/drivers/media/dvb-frontends/stv0900_core.c +++ b/drivers/media/dvb-frontends/stv0900_core.c @@ -1552,8 +1552,8 @@ static int stv0900_status(struct stv0900_internal *intp, bitrate = (stv0900_get_mclk_freq(intp, intp->quartz)/1000000) * (tsbitrate1_val << 8 | tsbitrate0_val); bitrate /= 16384; - dprintk("TS bitrate = %d Mbit/sec \n", bitrate); - }; + dprintk("TS bitrate = %d Mbit/sec\n", bitrate); + } return locked; } diff --git a/drivers/media/dvb-frontends/tda8083.c b/drivers/media/dvb-frontends/tda8083.c index 15912c96926..9d08350fe4b 100644 --- a/drivers/media/dvb-frontends/tda8083.c +++ b/drivers/media/dvb-frontends/tda8083.c @@ -175,7 +175,7 @@ static void tda8083_wait_diseqc_fifo (struct tda8083_state* state, int timeout) !(tda8083_readreg(state, 0x02) & 0x80)) { msleep(50); - }; + } } static int tda8083_set_tone (struct tda8083_state* state, fe_sec_tone_mode_t tone) @@ -215,7 +215,7 @@ static int tda8083_send_diseqc_burst (struct tda8083_state* state, fe_sec_mini_c break; default: return -EINVAL; - }; + } tda8083_wait_diseqc_fifo (state, 100); diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c index d8eac3e30a7..2cee69e3418 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.c +++ b/drivers/media/i2c/cx25840/cx25840-core.c @@ -599,7 +599,7 @@ static void cx23885_initialize(struct i2c_client *client) cx25840_write4(client, 0x114, 0x01bf0c9e); cx25840_write4(client, 0x110, 0x000a030c); break; - }; + } /* ADC2 input select */ cx25840_write(client, 0x102, 0x10); diff --git a/drivers/media/pci/bt8xx/dst_ca.c b/drivers/media/pci/bt8xx/dst_ca.c index ee3884fbc9c..7d96fab7d24 100644 --- a/drivers/media/pci/bt8xx/dst_ca.c +++ b/drivers/media/pci/bt8xx/dst_ca.c @@ -646,7 +646,7 @@ static long dst_ca_ioctl(struct file *file, unsigned int cmd, unsigned long ioct dprintk(verbose, DST_CA_INFO, 1, " -->CA_SET_PID Success !"); default: result = -EOPNOTSUPP; - }; + } free_mem_and_exit: kfree (p_ca_message); kfree (p_ca_slot_info); diff --git a/drivers/media/pci/cx23885/altera-ci.c b/drivers/media/pci/cx23885/altera-ci.c index aee7f0dacff..495781ee471 100644 --- a/drivers/media/pci/cx23885/altera-ci.c +++ b/drivers/media/pci/cx23885/altera-ci.c @@ -416,7 +416,7 @@ static void netup_read_ci_status(struct work_struct *work) DVB_CA_EN50221_POLL_CAM_READY : 0); ci_dbg_print("%s: setting CI[1] status = 0x%x\n", __func__, inter->state[1]->status); - }; + } if (inter->state[0] != NULL) { inter->state[0]->status = @@ -425,7 +425,7 @@ static void netup_read_ci_status(struct work_struct *work) DVB_CA_EN50221_POLL_CAM_READY : 0); ci_dbg_print("%s: setting CI[0] status = 0x%x\n", __func__, inter->state[0]->status); - }; + } } /* CI irq handler */ diff --git a/drivers/media/pci/cx23885/cimax2.c b/drivers/media/pci/cx23885/cimax2.c index c9f15d6dec4..6617774a326 100644 --- a/drivers/media/pci/cx23885/cimax2.c +++ b/drivers/media/pci/cx23885/cimax2.c @@ -193,7 +193,7 @@ int netup_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot, 0, &store, 1); if (ret != 0) return ret; - }; + } state->current_ci_flag = flag; mutex_lock(&dev->gpio_lock); diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c index d803bba0952..666f83b2f3c 100644 --- a/drivers/media/pci/cx88/cx88-dvb.c +++ b/drivers/media/pci/cx88/cx88-dvb.c @@ -896,7 +896,7 @@ static int samsung_smt_7020_set_voltage(struct dvb_frontend *fe, break; default: return -EINVAL; - }; + } return (i2c_transfer(&dev->core->i2c_adap, &msg, 1) == 1) ? 0 : -EIO; } diff --git a/drivers/media/pci/cx88/cx88-mpeg.c b/drivers/media/pci/cx88/cx88-mpeg.c index cd5386ee210..95c0c47718f 100644 --- a/drivers/media/pci/cx88/cx88-mpeg.c +++ b/drivers/media/pci/cx88/cx88-mpeg.c @@ -450,7 +450,7 @@ static irqreturn_t cx8802_irq(int irq, void *dev_id) cx88_core_irq(core,status); if (status & PCI_INT_TSINT) cx8802_mpeg_irq(dev); - }; + } if (MAX_IRQ_LOOP == loop) { dprintk( 0, "clearing mask\n" ); printk(KERN_WARNING "%s/0: irq loop -- clearing mask\n", diff --git a/drivers/media/pci/cx88/cx88-tvaudio.c b/drivers/media/pci/cx88/cx88-tvaudio.c index 770ec05b5e9..424fd97495d 100644 --- a/drivers/media/pci/cx88/cx88-tvaudio.c +++ b/drivers/media/pci/cx88/cx88-tvaudio.c @@ -373,7 +373,7 @@ static void set_audio_standard_NICAM(struct cx88_core *core, u32 mode) set_audio_registers(core, nicam_bgdki_common); set_audio_registers(core, nicam_default); break; - }; + } mode |= EN_DMTRX_LR | EN_DMTRX_BYPASS; set_audio_finish(core, mode); @@ -639,7 +639,7 @@ static void set_audio_standard_A2(struct cx88_core *core, u32 mode) dprintk("%s Warning: wrong value\n", __func__); return; break; - }; + } mode |= EN_FMRADIO_EN_RDS | EN_DMTRX_SUMDIFF; set_audio_finish(core, mode); diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c index a146d50d779..05171457bf2 100644 --- a/drivers/media/pci/cx88/cx88-video.c +++ b/drivers/media/pci/cx88/cx88-video.c @@ -1535,7 +1535,7 @@ static irqreturn_t cx8800_irq(int irq, void *dev_id) cx88_core_irq(core,status); if (status & PCI_INT_VIDINT) cx8800_vid_irq(dev); - }; + } if (10 == loop) { printk(KERN_WARNING "%s/0: irq loop -- clearing mask\n", core->name); diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 22f8758d047..4a77124ee70 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -1204,7 +1204,7 @@ int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, str break; default: /* nothing */; - }; + } switch (c->id) { case V4L2_CID_BRIGHTNESS: dev->ctl_bright = c->value; diff --git a/drivers/media/platform/exynos-gsc/gsc-regs.c b/drivers/media/platform/exynos-gsc/gsc-regs.c index 0d8625f03a3..0146b354dc2 100644 --- a/drivers/media/platform/exynos-gsc/gsc-regs.c +++ b/drivers/media/platform/exynos-gsc/gsc-regs.c @@ -212,7 +212,7 @@ void gsc_hw_set_in_image_format(struct gsc_ctx *ctx) else cfg |= GSC_IN_YUV422_3P; break; - }; + } writel(cfg, dev->regs + GSC_IN_CON); } @@ -332,7 +332,7 @@ void gsc_hw_set_out_image_format(struct gsc_ctx *ctx) case 3: cfg |= GSC_OUT_YUV420_3P; break; - }; + } end_set: writel(cfg, dev->regs + GSC_OUT_CON); diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c index e5024cfd27a..4ef55ec8045 100644 --- a/drivers/media/radio/si470x/radio-si470x-i2c.c +++ b/drivers/media/radio/si470x/radio-si470x-i2c.c @@ -308,7 +308,7 @@ static irqreturn_t si470x_i2c_interrupt(int irq, void *dev_id) READCHAN_BLERD) >> 10; rds = radio->registers[RDSD]; break; - }; + } /* Fill the V4L2 RDS buffer */ put_unaligned_le16(rds, &tmpbuf); diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c index be076f7181e..62f3edec39b 100644 --- a/drivers/media/radio/si470x/radio-si470x-usb.c +++ b/drivers/media/radio/si470x/radio-si470x-usb.c @@ -446,7 +446,7 @@ static void si470x_int_in_callback(struct urb *urb) READCHAN_BLERD) >> 10; rds = radio->registers[RDSD]; break; - }; + } /* Fill the V4L2 RDS buffer */ put_unaligned_le16(rds, &tmpbuf); diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c index 824f1911ee2..3d7526e28d4 100644 --- a/drivers/media/usb/dvb-usb-v2/af9015.c +++ b/drivers/media/usb/dvb-usb-v2/af9015.c @@ -500,7 +500,7 @@ static int af9015_read_config(struct dvb_usb_device *d) case 3: state->af9013_config[i].clock = 25000000; break; - }; + } dev_dbg(&d->udev->dev, "%s: [%d] xtal=%d set clock=%d\n", __func__, i, val, state->af9013_config[i].clock); @@ -568,7 +568,7 @@ static int af9015_read_config(struct dvb_usb_device *d) "supported, please report!\n", KBUILD_MODNAME, val); return -ENODEV; - }; + } state->af9013_config[i].tuner = val; dev_dbg(&d->udev->dev, "%s: [%d] tuner id=%d\n", diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index aabd3fc03ea..ea27eaff4e3 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -520,7 +520,7 @@ static int af9035_read_config(struct dvb_usb_device *d) dev_warn(&d->udev->dev, "%s: tuner id=%02x not " \ "supported, please report!", KBUILD_MODNAME, tmp); - }; + } /* tuner IF frequency */ ret = af9035_rd_reg(d, EEPROM_1_IFFREQ_L + eeprom_shift, &tmp); -- cgit v1.2.3-70-g09d2