X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fmedia%2Fvideo%2Fsaa7115.c;h=dd1943987ce6c8ce794002441bb823c2ece9b372;hb=27d35fc3fb06284edec8a3c9f6872a1ce7405a48;hp=974179d4d3895b2dd3135b245d1e5158c4a8e213;hpb=183d896a03e1f0e4ab69d75f123d33a62b47cf03;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c index 974179d..dd19439 100644 --- a/drivers/media/video/saa7115.c +++ b/drivers/media/video/saa7115.c @@ -44,7 +44,9 @@ #include #include #include -#include +#include +#include +#include #include #include @@ -55,7 +57,7 @@ MODULE_AUTHOR( "Maxim Yevtyushkin, Kevin Thayer, Chris Kennedy, " "Hans Verkuil, Mauro Carvalho Chehab"); MODULE_LICENSE("GPL"); -static int debug = 0; +static int debug; module_param(debug, bool, 0644); MODULE_PARM_DESC(debug, "Debug level (0-1)"); @@ -65,12 +67,13 @@ static unsigned short normal_i2c[] = { 0x42 >> 1, 0x40 >> 1, /* SAA7114, SAA7115 and SAA7118 */ I2C_CLIENT_END }; - I2C_CLIENT_INSMOD; struct saa711x_state { + struct v4l2_subdev sd; v4l2_std_id std; int input; + int output; int enable; int radio; int bright; @@ -79,7 +82,7 @@ struct saa711x_state { int sat; int width; int height; - enum v4l2_chip_ident ident; + u32 ident; u32 audclk_freq; u32 crystal_freq; u8 ucgc; @@ -87,10 +90,17 @@ struct saa711x_state { u8 apll; }; +static inline struct saa711x_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa711x_state, sd); +} + /* ----------------------------------------------------------------------- */ -static inline int saa711x_write(struct i2c_client *client, u8 reg, u8 value) +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); } @@ -126,9 +136,9 @@ static int saa711x_has_reg(const int id, const u8 reg) return 1; } -static int saa711x_writeregs(struct i2c_client *client, const unsigned char *regs) +static int saa711x_writeregs(struct v4l2_subdev *sd, const unsigned char *regs) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); unsigned char reg, data; while (*regs != 0x00) { @@ -137,18 +147,20 @@ static int saa711x_writeregs(struct i2c_client *client, const unsigned char *reg /* 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(client, reg, data) < 0) + if (saa711x_has_reg(state->ident, reg)) { + if (saa711x_write(sd, reg, data) < 0) return -1; } else { - v4l_dbg(1, debug, client, "tried to access reserved reg 0x%02x\n", reg); + v4l2_dbg(1, debug, sd, "tried to access reserved reg 0x%02x\n", reg); } } return 0; } -static inline int saa711x_read(struct i2c_client *client, u8 reg) +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); } @@ -599,7 +611,7 @@ static int saa711x_odd_parity(u8 c) return c & 1; } -static int saa711x_decode_vps(u8 * dst, u8 * p) +static int saa711x_decode_vps(u8 *dst, u8 *p) { static const u8 biphase_tbl[] = { 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, @@ -646,7 +658,7 @@ static int saa711x_decode_vps(u8 * dst, u8 * p) return err & 0xf0; } -static int saa711x_decode_wss(u8 * p) +static int saa711x_decode_wss(u8 *p) { static const int wss_bits[8] = { 0, 0, 0, 1, 0, 1, 1, 1 @@ -673,9 +685,9 @@ static int saa711x_decode_wss(u8 * p) return wss; } -static int saa711x_set_audio_clock_freq(struct i2c_client *client, u32 freq) +static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); u32 acpf; u32 acni; u32 hz; @@ -683,10 +695,10 @@ static int saa711x_set_audio_clock_freq(struct i2c_client *client, u32 freq) 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)) + if (!saa711x_has_reg(state->ident, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD)) return 0; - v4l_dbg(1, debug, client, "set audio clock freq: %d\n", freq); + v4l2_dbg(1, debug, sd, "set audio clock freq: %d\n", freq); /* sanity check */ if (freq < 32000 || freq > 48000) @@ -713,66 +725,66 @@ static int saa711x_set_audio_clock_freq(struct i2c_client *client, u32 freq) if (state->apll) acc |= 0x08; - saa711x_write(client, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03); - saa711x_write(client, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10); - saa711x_write(client, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc); + 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(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff); - saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+1, + 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(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+2, + saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+2, (acpf >> 16) & 0x03); - saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC, acni & 0xff); - saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC+1, (acni >> 8) & 0xff); - saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC+2, (acni >> 16) & 0x3f); + 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_set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int saa711x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: if (ctrl->value < 0 || ctrl->value > 255) { - v4l_err(client, "invalid brightness setting %d\n", ctrl->value); + v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value); return -ERANGE; } state->bright = ctrl->value; - saa711x_write(client, R_0A_LUMA_BRIGHT_CNTL, state->bright); + saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, state->bright); break; case V4L2_CID_CONTRAST: if (ctrl->value < 0 || ctrl->value > 127) { - v4l_err(client, "invalid contrast setting %d\n", ctrl->value); + v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value); return -ERANGE; } state->contrast = ctrl->value; - saa711x_write(client, R_0B_LUMA_CONTRAST_CNTL, state->contrast); + saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, state->contrast); break; case V4L2_CID_SATURATION: if (ctrl->value < 0 || ctrl->value > 127) { - v4l_err(client, "invalid saturation setting %d\n", ctrl->value); + v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value); return -ERANGE; } state->sat = ctrl->value; - saa711x_write(client, R_0C_CHROMA_SAT_CNTL, state->sat); + saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, state->sat); break; case V4L2_CID_HUE: - if (ctrl->value < -127 || ctrl->value > 127) { - v4l_err(client, "invalid hue setting %d\n", ctrl->value); + if (ctrl->value < -128 || ctrl->value > 127) { + v4l2_err(sd, "invalid hue setting %d\n", ctrl->value); return -ERANGE; } state->hue = ctrl->value; - saa711x_write(client, R_0D_CHROMA_HUE_CNTL, state->hue); + saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, state->hue); break; default: @@ -782,9 +794,9 @@ static int saa711x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *c return 0; } -static int saa711x_get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int saa711x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: @@ -806,16 +818,16 @@ static int saa711x_get_v4lctrl(struct i2c_client *client, struct v4l2_control *c return 0; } -static int saa711x_set_size(struct i2c_client *client, int width, int height) +static int saa711x_set_size(struct v4l2_subdev *sd, int width, int height) { - struct saa711x_state *state = i2c_get_clientdata(client); + 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; - v4l_dbg(1, debug, client, "decoder set size to %ix%i\n",width,height); + 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)) @@ -823,7 +835,7 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height) if ((height < 1) || (height > Vsrc)) return -EINVAL; - if (!saa711x_has_reg(state->ident,R_D0_B_HORIZ_PRESCALING)) { + 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; @@ -841,22 +853,22 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height) /* Set output width/height */ /* width */ - saa711x_write(client, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, + saa711x_write(sd, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, (u8) (width & 0xff)); - saa711x_write(client, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, + saa711x_write(sd, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, (u8) ((width >> 8) & 0xff)); /* Vertical Scaling uses height/2 */ - res=height/2; + res = height / 2; /* On 60Hz, it is using a higher Vertical Output Size */ if (!is_50hz) - res+=(VRES_60HZ-480)>>1; + res += (VRES_60HZ - 480) >> 1; /* height */ - saa711x_write(client, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH, + saa711x_write(sd, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH, (u8) (res & 0xff)); - saa711x_write(client, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB, + saa711x_write(sd, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB, (u8) ((res >> 8) & 0xff)); /* Scaling settings */ @@ -867,54 +879,54 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height) HFSC = (int)((1024 * 720) / (HPSC * width)); /* FIXME hardcodes to "Task B" * write H prescaler integer */ - saa711x_write(client, R_D0_B_HORIZ_PRESCALING, + saa711x_write(sd, R_D0_B_HORIZ_PRESCALING, (u8) (HPSC & 0x3f)); - v4l_dbg(1, debug, client, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC); + v4l2_dbg(1, debug, sd, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC); /* write H fine-scaling (luminance) */ - saa711x_write(client, R_D8_B_HORIZ_LUMA_SCALING_INC, + saa711x_write(sd, R_D8_B_HORIZ_LUMA_SCALING_INC, (u8) (HFSC & 0xff)); - saa711x_write(client, R_D9_B_HORIZ_LUMA_SCALING_INC_MSB, + 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(client, R_DC_B_HORIZ_CHROMA_SCALING, + saa711x_write(sd, R_DC_B_HORIZ_CHROMA_SCALING, (u8) ((HFSC >> 1) & 0xff)); - saa711x_write(client, R_DD_B_HORIZ_CHROMA_SCALING_MSB, + saa711x_write(sd, R_DD_B_HORIZ_CHROMA_SCALING_MSB, (u8) ((HFSC >> 9) & 0xff)); VSCY = (int)((1024 * Vsrc) / height); - v4l_dbg(1, debug, client, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY); + v4l2_dbg(1, debug, sd, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY); /* Correct Contrast and Luminance */ - saa711x_write(client, R_D5_B_LUMA_CONTRAST_CNTL, + saa711x_write(sd, R_D5_B_LUMA_CONTRAST_CNTL, (u8) (64 * 1024 / VSCY)); - saa711x_write(client, R_D6_B_CHROMA_SATURATION_CNTL, + saa711x_write(sd, R_D6_B_CHROMA_SATURATION_CNTL, (u8) (64 * 1024 / VSCY)); /* write V fine-scaling (luminance) */ - saa711x_write(client, R_E0_B_VERT_LUMA_SCALING_INC, + saa711x_write(sd, R_E0_B_VERT_LUMA_SCALING_INC, (u8) (VSCY & 0xff)); - saa711x_write(client, R_E1_B_VERT_LUMA_SCALING_INC_MSB, + saa711x_write(sd, R_E1_B_VERT_LUMA_SCALING_INC_MSB, (u8) ((VSCY >> 8) & 0xff)); /* write V fine-scaling (chrominance) */ - saa711x_write(client, R_E2_B_VERT_CHROMA_SCALING_INC, + saa711x_write(sd, R_E2_B_VERT_CHROMA_SCALING_INC, (u8) (VSCY & 0xff)); - saa711x_write(client, R_E3_B_VERT_CHROMA_SCALING_INC_MSB, + saa711x_write(sd, R_E3_B_VERT_CHROMA_SCALING_INC_MSB, (u8) ((VSCY >> 8) & 0xff)); - saa711x_writeregs(client, saa7115_cfg_reset_scaler); + saa711x_writeregs(sd, saa7115_cfg_reset_scaler); /* Activates task "B" */ - saa711x_write(client, R_80_GLOBAL_CNTL_1, - saa711x_read(client,R_80_GLOBAL_CNTL_1)|0x20); + 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 i2c_client *client, v4l2_std_id std) +static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); /* Prevent unnecessary standard changes. During a standard change the I-Port is temporarily disabled. Any devices @@ -930,13 +942,13 @@ static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std) // This works for NTSC-M, SECAM-L and the 50Hz PAL variants. if (std & V4L2_STD_525_60) { - v4l_dbg(1, debug, client, "decoder set standard 60 Hz\n"); - saa711x_writeregs(client, saa7115_cfg_60hz_video); - saa711x_set_size(client,720,480); + 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 { - v4l_dbg(1, debug, client, "decoder set standard 50 Hz\n"); - saa711x_writeregs(client, saa7115_cfg_50hz_video); - saa711x_set_size(client,720,576); + 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 @@ -950,98 +962,44 @@ static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std) */ if (state->ident == V4L2_IDENT_SAA7111 || state->ident == V4L2_IDENT_SAA7113) { - u8 reg = saa711x_read(client, R_0E_CHROMA_CNTL_1) & 0x8f; + 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_N) { + } 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(client, R_0E_CHROMA_CNTL_1, reg); + saa711x_write(sd, R_0E_CHROMA_CNTL_1, reg); } else { /* restart task B if needed */ - int taskb = saa711x_read(client, R_80_GLOBAL_CNTL_1) & 0x10; + int taskb = saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10; if (taskb && state->ident == V4L2_IDENT_SAA7114) { - saa711x_writeregs(client, saa7115_cfg_vbi_on); + saa711x_writeregs(sd, saa7115_cfg_vbi_on); } /* switch audio mode too! */ - saa711x_set_audio_clock_freq(client, state->audclk_freq); + saa711x_s_clock_freq(sd, state->audclk_freq); } } -static v4l2_std_id saa711x_get_v4lstd(struct i2c_client *client) -{ - struct saa711x_state *state = i2c_get_clientdata(client); - - return state->std; -} - -static void saa711x_log_status(struct i2c_client *client) -{ - struct saa711x_state *state = i2c_get_clientdata(client); - int reg1e, reg1f; - int signalOk; - int vcr; - - v4l_info(client, "Audio frequency: %d Hz\n", state->audclk_freq); - if (state->ident != V4L2_IDENT_SAA7115) { - /* status for the saa7114 */ - reg1f = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC); - signalOk = (reg1f & 0xc1) == 0x81; - v4l_info(client, "Video signal: %s\n", signalOk ? "ok" : "bad"); - v4l_info(client, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); - return; - } - - /* status for the saa7115 */ - reg1e = saa711x_read(client, R_1E_STATUS_BYTE_1_VD_DEC); - reg1f = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC); - - signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80; - vcr = !(reg1f & 0x10); - - if (state->input >= 6) { - v4l_info(client, "Input: S-Video %d\n", state->input - 6); - } else { - v4l_info(client, "Input: Composite %d\n", state->input); - } - v4l_info(client, "Video signal: %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad"); - v4l_info(client, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); - - switch (reg1e & 0x03) { - case 1: - v4l_info(client, "Detected format: NTSC\n"); - break; - case 2: - v4l_info(client, "Detected format: PAL\n"); - break; - case 3: - v4l_info(client, "Detected format: SECAM\n"); - break; - default: - v4l_info(client, "Detected format: BW/No color\n"); - break; - } - v4l_info(client, "Width, Height: %d, %d\n", state->width, state->height); -} - /* setup the sliced VBI lcr registers according to the sliced VBI format */ -static void saa711x_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_format *fmt) +static void saa711x_set_lcr(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt) { - struct saa711x_state *state = i2c_get_clientdata(client); + 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)) + if (!saa711x_has_reg(state->ident, R_41_LCR_BASE)) return; #else @@ -1053,7 +1011,7 @@ static void saa711x_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_fo for (i = 0; i <= 23; i++) lcr[i] = 0xff; - if (fmt->service_set == 0) { + if (fmt == NULL) { /* raw VBI */ if (is_50hz) for (i = 6; i <= 23; i++) @@ -1105,16 +1063,16 @@ static void saa711x_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_fo /* write the lcr registers */ for (i = 2; i <= 23; i++) { - saa711x_write(client, i - 2 + R_41_LCR_BASE, lcr[i]); + saa711x_write(sd, i - 2 + R_41_LCR_BASE, lcr[i]); } /* enable/disable raw VBI capturing */ - saa711x_writeregs(client, fmt->service_set == 0 ? + saa711x_writeregs(sd, fmt == NULL ? saa7115_cfg_vbi_on : saa7115_cfg_vbi_off); } -static int saa711x_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) +static int saa711x_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { static u16 lcr2vbi[] = { 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ @@ -1130,10 +1088,10 @@ static int saa711x_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt return -EINVAL; memset(sliced, 0, sizeof(*sliced)); /* done if using raw VBI */ - if (saa711x_read(client, R_80_GLOBAL_CNTL_1) & 0x10) + if (saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10) return 0; for (i = 2; i <= 23; i++) { - u8 v = saa711x_read(client, i - 2 + R_41_LCR_BASE); + 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]; @@ -1143,16 +1101,20 @@ static int saa711x_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt return 0; } -static int saa711x_set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) +static int saa711x_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { if (fmt->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { - saa711x_set_lcr(client, &fmt->fmt.sliced); + saa711x_set_lcr(sd, &fmt->fmt.sliced); + return 0; + } + if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + saa711x_set_lcr(sd, NULL); return 0; } if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - return saa711x_set_size(client,fmt->fmt.pix.width,fmt->fmt.pix.height); + return saa711x_set_size(sd, fmt->fmt.pix.width, fmt->fmt.pix.height); } /* Decode the sliced VBI data stream as created by the saa7115. @@ -1161,13 +1123,12 @@ static int saa711x_set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt 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 void saa711x_decode_vbi_line(struct i2c_client *client, - struct v4l2_decode_vbi_line *vbi) +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 }; - struct saa711x_state *state = i2c_get_clientdata(client); u8 *p = vbi->p; u32 wss; int id1, id2; /* the ID1 and ID2 bytes from the internal header */ @@ -1194,7 +1155,7 @@ static void saa711x_decode_vbi_line(struct i2c_client *client, /* 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; + return 0; /* decode payloads */ switch (id2) { @@ -1203,288 +1164,393 @@ static void saa711x_decode_vbi_line(struct i2c_client *client, break; case 4: if (!saa711x_odd_parity(p[0]) || !saa711x_odd_parity(p[1])) - return; + return 0; vbi->type = V4L2_SLICED_CAPTION_525; break; case 5: wss = saa711x_decode_wss(p); if (wss == -1) - return; + 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; + return 0; vbi->type = V4L2_SLICED_VPS; break; default: - return; + break; } + return 0; } /* ============ SAA7115 AUDIO settings (end) ============= */ -static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *arg) +static int saa711x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) { - struct saa711x_state *state = i2c_get_clientdata(client); - int *iarg = arg; - - /* ioctls to allow direct access to the saa7115 registers for testing */ - switch (cmd) { - case VIDIOC_S_FMT: - return saa711x_set_v4lfmt(client, (struct v4l2_format *)arg); + struct saa711x_state *state = to_state(sd); + int status; - case VIDIOC_G_FMT: - return saa711x_get_v4lfmt(client, (struct v4l2_format *)arg); - - case VIDIOC_INT_AUDIO_CLOCK_FREQ: - return saa711x_set_audio_clock_freq(client, *(u32 *)arg); - - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *vt = arg; - int status; + if (state->radio) + return 0; + status = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); - if (state->radio) - break; - status = saa711x_read(client, 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; +} - v4l_dbg(1, debug, client, "status: 0x%02x\n", status); - vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0; - break; +static int saa711x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill_std(qc); + default: + return -EINVAL; } +} - case VIDIOC_LOG_STATUS: - saa711x_log_status(client); - break; +static int saa711x_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct saa711x_state *state = to_state(sd); - case VIDIOC_G_CTRL: - return saa711x_get_v4lctrl(client, (struct v4l2_control *)arg); + state->radio = 0; + saa711x_set_v4lstd(sd, std); + return 0; +} - case VIDIOC_S_CTRL: - return saa711x_set_v4lctrl(client, (struct v4l2_control *)arg); +static int saa711x_s_radio(struct v4l2_subdev *sd) +{ + struct saa711x_state *state = to_state(sd); - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; + state->radio = 1; + return 0; +} - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill_std(qc); - default: - return -EINVAL; - } +static int saa711x_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct saa711x_state *state = to_state(sd); + u32 input = route->input; + u8 mask = (state->ident == V4L2_IDENT_SAA7111) ? 0xf8 : 0xf0; + + v4l2_dbg(1, debug, sd, "decoder set input %d output %d\n", route->input, route->output); + /* saa7111/3 does not have these inputs */ + if ((state->ident == V4L2_IDENT_SAA7113 || + state->ident == V4L2_IDENT_SAA7111) && + (route->input == SAA7115_COMPOSITE4 || + route->input == SAA7115_COMPOSITE5)) { + return -EINVAL; + } + if (route->input > SAA7115_SVIDEO3) + return -EINVAL; + if (route->output > SAA7115_IPORT_ON) + return -EINVAL; + if (state->input == route->input && state->output == route->output) + return 0; + v4l2_dbg(1, debug, sd, "now setting %s input %s output\n", + (route->input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite", + (route->output == SAA7115_IPORT_ON) ? "iport on" : "iport off"); + state->input = route->input; + + /* saa7111 has slightly different input numbering */ + if (state->ident == V4L2_IDENT_SAA7111) { + 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) | + ((route->output & 0xc0) ^ 0x40)); + saa711x_write(sd, R_13_RT_X_PORT_OUT_CNTL, + (saa711x_read(sd, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) | + ((route->output & 2) ? 0x0a : 0)); } - case VIDIOC_G_STD: - *(v4l2_std_id *)arg = saa711x_get_v4lstd(client); - break; + /* 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 = route->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; +} - case VIDIOC_S_STD: - state->radio = 0; - saa711x_set_v4lstd(client, *(v4l2_std_id *)arg); - break; +static int saa711x_s_gpio(struct v4l2_subdev *sd, u32 val) +{ + struct saa711x_state *state = to_state(sd); - case AUDC_SET_RADIO: - state->radio = 1; - break; + if (state->ident != V4L2_IDENT_SAA7111) + return -EINVAL; + saa711x_write(sd, 0x11, (saa711x_read(sd, 0x11) & 0x7f) | + (val ? 0x80 : 0)); + return 0; +} - case VIDIOC_INT_G_VIDEO_ROUTING: - { - struct v4l2_routing *route = arg; +static int saa711x_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct saa711x_state *state = to_state(sd); - route->input = state->input; - route->output = 0; - break; + v4l2_dbg(1, debug, sd, "%s output\n", + enable ? "enable" : "disable"); + + if (state->enable != enable) { + state->enable = enable; + saa711x_write(sd, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, + state->enable); } + return 0; +} - case VIDIOC_INT_S_VIDEO_ROUTING: - { - struct v4l2_routing *route = arg; +static int saa711x_s_crystal_freq(struct v4l2_subdev *sd, struct v4l2_crystal_freq *freq) +{ + struct saa711x_state *state = to_state(sd); - v4l_dbg(1, debug, client, "decoder set input %d\n", route->input); - /* saa7113 does not have these inputs */ - if (state->ident == V4L2_IDENT_SAA7113 && - (route->input == SAA7115_COMPOSITE4 || - route->input == SAA7115_COMPOSITE5)) { - return -EINVAL; + if (freq->freq != SAA7115_FREQ_32_11_MHZ && + freq->freq != SAA7115_FREQ_24_576_MHZ) + return -EINVAL; + state->crystal_freq = freq->freq; + state->cgcdiv = (freq->flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4; + state->ucgc = (freq->flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0; + state->apll = (freq->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; } - if (route->input > SAA7115_SVIDEO3) - return -EINVAL; - if (state->input == route->input) - break; - v4l_dbg(1, debug, client, "now setting %s input\n", - (route->input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite"); - state->input = route->input; - - /* select mode */ - saa711x_write(client, R_02_INPUT_CNTL_1, - (saa711x_read(client, R_02_INPUT_CNTL_1) & 0xf0) | - state->input); - - /* bypass chrominance trap for S-Video modes */ - saa711x_write(client, R_09_LUMA_CNTL, - (saa711x_read(client, R_09_LUMA_CNTL) & 0x7f) | - (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0)); - break; + /* 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; } +} - case VIDIOC_STREAMON: - case VIDIOC_STREAMOFF: - v4l_dbg(1, debug, client, "%s output\n", - (cmd == VIDIOC_STREAMON) ? "enable" : "disable"); +#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 (state->enable != (cmd == VIDIOC_STREAMON)) { - state->enable = (cmd == VIDIOC_STREAMON); - saa711x_write(client, - R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, - state->enable); - } - break; + 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; +} - case VIDIOC_INT_S_CRYSTAL_FREQ: - { - struct v4l2_crystal_freq *freq = arg; +static int saa711x_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); - if (freq->freq != SAA7115_FREQ_32_11_MHZ && - freq->freq != SAA7115_FREQ_24_576_MHZ) - return -EINVAL; - state->crystal_freq = freq->freq; - state->cgcdiv = (freq->flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4; - state->ucgc = (freq->flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0; - state->apll = (freq->flags & SAA7115_FREQ_FL_APLL) ? 1 : 0; - saa711x_set_audio_clock_freq(client, state->audclk_freq); - break; - } + 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 - case VIDIOC_INT_DECODE_VBI_LINE: - saa711x_decode_vbi_line(client, arg); - break; +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); - case VIDIOC_INT_RESET: - v4l_dbg(1, debug, client, "decoder RESET\n"); - saa711x_writeregs(client, saa7115_cfg_reset_scaler); - break; + return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0); +} - case VIDIOC_INT_G_VBI_DATA: - { - struct v4l2_sliced_vbi_data *data = arg; +static int saa711x_log_status(struct v4l2_subdev *sd) +{ + struct saa711x_state *state = to_state(sd); + int reg1e, reg1f; + int signalOk; + int vcr; - switch (data->id) { - case V4L2_SLICED_WSS_625: - if (saa711x_read(client, 0x6b) & 0xc0) - return -EIO; - data->data[0] = saa711x_read(client, 0x6c); - data->data[1] = saa711x_read(client, 0x6d); - return 0; - case V4L2_SLICED_CAPTION_525: - if (data->field == 0) { - /* CC */ - if (saa711x_read(client, 0x66) & 0xc0) - return -EIO; - data->data[0] = saa711x_read(client, 0x67); - data->data[1] = saa711x_read(client, 0x68); - return 0; - } - /* XDS */ - if (saa711x_read(client, 0x66) & 0x30) - return -EIO; - data->data[0] = saa711x_read(client, 0x69); - data->data[1] = saa711x_read(client, 0x6a); - return 0; - default: - return -EINVAL; - } - break; + 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; } -#ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_INT_G_REGISTER: - { - struct v4l2_register *reg = arg; + /* 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); - if (reg->i2c_id != I2C_DRIVERID_SAA711X) - return -EINVAL; - reg->val = saa711x_read(client, reg->reg & 0xff); - break; - } + signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80; + vcr = !(reg1f & 0x10); - case VIDIOC_INT_S_REGISTER: - { - struct v4l2_register *reg = arg; + 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"); - if (reg->i2c_id != I2C_DRIVERID_SAA711X) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - saa711x_write(client, reg->reg & 0xff, reg->val & 0xff); + switch (reg1e & 0x03) { + case 1: + v4l2_info(sd, "Detected format: NTSC\n"); break; - } -#endif - - case VIDIOC_INT_G_CHIP_IDENT: - *iarg = state->ident; + case 2: + v4l2_info(sd, "Detected format: PAL\n"); + break; + case 3: + v4l2_info(sd, "Detected format: SECAM\n"); break; - default: - return -EINVAL; + v4l2_info(sd, "Detected format: BW/No color\n"); + break; } - + v4l2_info(sd, "Width, Height: %d, %d\n", state->width, state->height); return 0; } +static int saa711x_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + /* ----------------------------------------------------------------------- */ -static struct i2c_driver i2c_driver_saa711x; +static const struct v4l2_subdev_core_ops saa711x_core_ops = { + .log_status = saa711x_log_status, + .g_chip_ident = saa711x_g_chip_ident, + .g_ctrl = saa711x_g_ctrl, + .s_ctrl = saa711x_s_ctrl, + .queryctrl = saa711x_queryctrl, + .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_std = saa711x_s_std, + .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 int saa711x_attach(struct i2c_adapter *adapter, int address, int kind) +static const struct v4l2_subdev_video_ops saa711x_video_ops = { + .s_routing = saa711x_s_routing, + .s_crystal_freq = saa711x_s_crystal_freq, + .g_fmt = saa711x_g_fmt, + .s_fmt = saa711x_s_fmt, + .g_vbi_data = saa711x_g_vbi_data, + .decode_vbi_line = saa711x_decode_vbi_line, + .s_stream = saa711x_s_stream, +}; + +static const struct v4l2_subdev_ops saa711x_ops = { + .core = &saa711x_core_ops, + .tuner = &saa711x_tuner_ops, + .audio = &saa711x_audio_ops, + .video = &saa711x_video_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static int saa711x_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - struct i2c_client *client; struct saa711x_state *state; + struct v4l2_subdev *sd; int i; char name[17]; - u8 chip_id; + char chip_id; + int autodetect = !id || id->driver_data == 1; /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return 0; - - client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (client == 0) - return -ENOMEM; - client->addr = address; - client->adapter = adapter; - client->driver = &i2c_driver_saa711x; - snprintf(client->name, sizeof(client->name) - 1, "saa7115"); - - v4l_dbg(1, debug, client, "detecting saa7115 client on address 0x%x\n", address << 1); - - for (i=0;i<0x0f;i++) { - saa711x_write(client, 0, i); - name[i] = (saa711x_read(client, 0) &0x0f) +'0'; - if (name[i]>'9') - name[i]+='a'-'9'-1; + 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'; + name[i] = '\0'; - saa711x_write(client, 0, 5); - chip_id = saa711x_read(client, 0) & 0x0f; + chip_id = name[5]; - snprintf(client->name, sizeof(client->name) - 1, "saa711%d",chip_id); - v4l_info(client, "saa711%d found (%s) @ 0x%x (%s)\n", chip_id, name, address << 1, adapter->name); + /* Check whether this chip is part of the saa711x series */ + if (memcmp(name, "1f711", 5)) { + 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); - i2c_set_clientdata(client, state); - if (state == NULL) { - kfree(client); + if (state == NULL) return -ENOMEM; - } + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &saa711x_ops); state->input = -1; + state->output = SAA7115_IPORT_ON; state->enable = 1; state->radio = 0; state->bright = 128; @@ -1492,100 +1558,82 @@ static int saa711x_attach(struct i2c_adapter *adapter, int address, int kind) state->hue = 0; state->sat = 64; switch (chip_id) { - case 1: + case '1': state->ident = V4L2_IDENT_SAA7111; break; - case 3: + case '3': state->ident = V4L2_IDENT_SAA7113; break; - case 4: + case '4': state->ident = V4L2_IDENT_SAA7114; break; - case 5: + case '5': state->ident = V4L2_IDENT_SAA7115; break; - case 8: + case '8': state->ident = V4L2_IDENT_SAA7118; break; default: state->ident = V4L2_IDENT_SAA7111; - v4l_info(client, "WARNING: Chip is not known - Falling back to saa7111\n"); + v4l2_info(sd, "WARNING: Chip is not known - Falling back to saa7111\n"); } state->audclk_freq = 48000; - v4l_dbg(1, debug, client, "writing init values\n"); + 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: - saa711x_writeregs(client, saa7111_init); + saa711x_writeregs(sd, saa7111_init); break; case V4L2_IDENT_SAA7113: - saa711x_writeregs(client, saa7113_init); + saa711x_writeregs(sd, saa7113_init); break; default: state->crystal_freq = SAA7115_FREQ_32_11_MHZ; - saa711x_writeregs(client, saa7115_init_auto_input); + saa711x_writeregs(sd, saa7115_init_auto_input); } - saa711x_writeregs(client, saa7115_init_misc); - saa711x_set_v4lstd(client, V4L2_STD_NTSC); - - i2c_attach_client(client); - - v4l_dbg(1, debug, client, "status: (1E) 0x%02x, (1F) 0x%02x\n", - saa711x_read(client, R_1E_STATUS_BYTE_1_VD_DEC), saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC)); + if (state->ident != V4L2_IDENT_SAA7111) + saa711x_writeregs(sd, saa7115_init_misc); + saa711x_set_v4lstd(sd, V4L2_STD_NTSC); + 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_probe(struct i2c_adapter *adapter) -{ - if (adapter->class & I2C_CLASS_TV_ANALOG) - return i2c_probe(adapter, &addr_data, &saa711x_attach); - return 0; -} +/* ----------------------------------------------------------------------- */ -static int saa711x_detach(struct i2c_client *client) +static int saa711x_remove(struct i2c_client *client) { - struct saa711x_state *state = i2c_get_clientdata(client); - int err; - - err = i2c_detach_client(client); - if (err) { - return err; - } + struct v4l2_subdev *sd = i2c_get_clientdata(client); - kfree(state); - kfree(client); + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } -/* ----------------------------------------------------------------------- */ +static const struct i2c_device_id saa7115_id[] = { + { "saa7115_auto", 1 }, /* autodetect */ + { "saa7111", 0 }, + { "saa7113", 0 }, + { "saa7114", 0 }, + { "saa7115", 0 }, + { "saa7118", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa7115_id); -/* i2c implementation */ -static struct i2c_driver i2c_driver_saa711x = { - .driver = { - .name = "saa7115", - }, - .id = I2C_DRIVERID_SAA711X, - .attach_adapter = saa711x_probe, - .detach_client = saa711x_detach, +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = "saa7115", + .driverid = I2C_DRIVERID_SAA711X, .command = saa711x_command, + .probe = saa711x_probe, + .remove = saa711x_remove, + .legacy_class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL, + .id_table = saa7115_id, }; - - -static int __init saa711x_init_module(void) -{ - return i2c_add_driver(&i2c_driver_saa711x); -} - -static void __exit saa711x_cleanup_module(void) -{ - i2c_del_driver(&i2c_driver_saa711x); -} - -module_init(saa711x_init_module); -module_exit(saa711x_cleanup_module);