#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-i2c-drv-legacy.h>
#include <media/saa7115.h>
#include <asm/div64.h>
+#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 int debug = 0;
+static int debug;
module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "Debug level (0-1)");
struct saa711x_state {
v4l2_std_id std;
int input;
+ int output;
int enable;
int radio;
int bright;
int contrast;
int hue;
int sat;
- enum v4l2_chip_ident ident;
+ int width;
+ int height;
+ u32 ident;
u32 audclk_freq;
u32 crystal_freq;
u8 ucgc;
/* 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;
+
+ /* 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_SAA7111:
- if (reg>0x1f || reg==1 || reg==0x0f || reg==0x14 || reg==0x18
- || reg==0x19 || reg==0x1d || reg==0x1e)
- return 0;
case V4L2_IDENT_SAA7113:
- if (reg>0x62 || reg==0x14 || (reg>=0x18 && reg<=0x1e) ||
- (reg>=0x20 && reg<=0x3f) ||reg==0x5f )
- return 0;
+ return reg != 0x14 && (reg < 0x18 || reg > 0x1e) && (reg < 0x20 || reg > 0x3f) &&
+ reg != 0x5d && reg < 0x63;
case V4L2_IDENT_SAA7114:
- if (reg>=0xf0 || (reg>=0x1a && reg<=0x1e) ||
- (reg>=0x20 && reg<=0x2f) ||
- (reg>=0x63 && reg<=0x7f) )
- return 0;
+ 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:
- if ((reg>=0x20 && reg<=0x2f) || (reg==0x5c) ||
- (reg>=0xfc && reg<=0xfe) )
- return 0;
+ return (reg < 0x20 || reg > 0x2f) && reg != 0x65 && (reg < 0xfc || reg > 0xfe);
case V4L2_IDENT_SAA7118:
- if (reg>=0xf0 || (reg>=0x1a && reg<=0x1d) ||
- (reg>=0x63 && reg<=0x6f) )
- return 0;
+ 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;
}
-
- /* Those registers are reserved for all family */
- if (unlikely((reg>=0x20 && reg<=0x22) ||
- (reg>=0x26 && reg<=0x28) ||
- (reg>=0x3b && reg<=0x3f) || (reg==0x5f) ||
- (reg>=0x63 && reg<=0x6f) ) )
- return 0;
-
return 1;
}
if (saa711x_has_reg(state->ident,reg)) {
if (saa711x_write(client, reg, data) < 0)
return -1;
+ } else {
+ v4l_dbg(1, debug, client, "tried to access reserved reg 0x%02x\n", reg);
}
}
return 0;
/* ----------------------------------------------------------------------- */
/* SAA7111 initialization table */
-static const unsigned char saa7111_init_auto_input[] = {
+static const unsigned char saa7111_init[] = {
R_01_INC_DELAY, 0x00, /* reserved */
/*front end */
};
/* SAA7113 init codes */
-static const unsigned char saa7113_init_auto_input[] = {
+static const unsigned char saa7113_init[] = {
R_01_INC_DELAY, 0x08,
R_02_INPUT_CNTL_1, 0xc2,
R_03_INPUT_CNTL_2, 0x30,
/* 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_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 */
R_C9_B_VERT_INPUT_WINDOW_START_MSB, 0x00,
/* vwindow length 0xf8 = 248 */
- R_CA_B_VERT_INPUT_WINDOW_LENGTH, 0xf8,
- R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB, 0x00,
+ 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_F5_PULSGEN_LINE_LENGTH, 0xad,
R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG, 0x01,
- 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_80_GLOBAL_CNTL_1, 0x20, /* Activate only task "B", continuous mode (was 0xA0) */
- 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
};
R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, 0xd0,
R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x02,
- /* vsize 0x0120 = 288 */
- R_CE_B_VERT_OUTPUT_WINDOW_LENGTH, 0x20,
- R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB, 0x01,
-
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,
- 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 (was 0xD0) */
- 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,
- 0x82, 0x00, /* Reserved register - value should be zero*/
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_5E_SDID, 0x35,
R_02_INPUT_CNTL_1, 0x84, /* input tuner -> input 4, amplifier active */
- R_09_LUMA_CNTL, 0x53, /* 0x53, was 0x56 for 60hz. luminance control */
R_80_GLOBAL_CNTL_1, 0x20, /* enable task B */
R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0,
return 0;
}
+static int saa711x_set_size(struct i2c_client *client, int width, int height)
+{
+ struct saa711x_state *state = i2c_get_clientdata(client);
+ 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);
+
+ /* 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(client, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH,
+ (u8) (width & 0xff));
+ saa711x_write(client, 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(client, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH,
+ (u8) (res & 0xff));
+ saa711x_write(client, 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(client, R_D0_B_HORIZ_PRESCALING,
+ (u8) (HPSC & 0x3f));
+
+ v4l_dbg(1, debug, client, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC);
+ /* write H fine-scaling (luminance) */
+ saa711x_write(client, R_D8_B_HORIZ_LUMA_SCALING_INC,
+ (u8) (HFSC & 0xff));
+ saa711x_write(client, 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,
+ (u8) ((HFSC >> 1) & 0xff));
+ saa711x_write(client, 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);
+
+ /* Correct Contrast and Luminance */
+ saa711x_write(client, R_D5_B_LUMA_CONTRAST_CNTL,
+ (u8) (64 * 1024 / VSCY));
+ saa711x_write(client, 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,
+ (u8) (VSCY & 0xff));
+ saa711x_write(client, 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,
+ (u8) (VSCY & 0xff));
+ saa711x_write(client, R_E3_B_VERT_CHROMA_SCALING_INC_MSB,
+ (u8) ((VSCY >> 8) & 0xff));
+
+ saa711x_writeregs(client, saa7115_cfg_reset_scaler);
+
+ /* Activates task "B" */
+ saa711x_write(client, R_80_GLOBAL_CNTL_1,
+ saa711x_read(client,R_80_GLOBAL_CNTL_1) | 0x20);
+
+ return 0;
+}
+
static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std)
{
struct saa711x_state *state = i2c_get_clientdata(client);
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) {
v4l_dbg(1, debug, client, "decoder set standard 60 Hz\n");
saa711x_writeregs(client, saa7115_cfg_60hz_video);
+ saa711x_set_size(client, 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);
}
/* Register 0E - Bits D6-D4 on NO-AUTO mode
011 NTSC N (3.58MHz) PAL M (3.58MHz)
100 reserved NTSC-Japan (3.58MHz)
*/
- state->std = std;
-
if (state->ident == V4L2_IDENT_SAA7111 ||
state->ident == V4L2_IDENT_SAA7113) {
u8 reg = saa711x_read(client, 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);
} else {
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 */
return 0;
}
-static int saa711x_set_size(struct i2c_client *client, int width, int height)
-{
- struct saa711x_state *state = i2c_get_clientdata(client);
- 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);
-
- /* FIXME need better bounds checking here */
- if ((width < 1) || (width > 1440))
- return -EINVAL;
- if ((height < 1) || (height > 960))
- 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;
- }
- 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(client, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH,
- (u8) (width & 0xff));
- saa711x_write(client, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB,
- (u8) ((width >> 8) & 0xff));
-
- if (height == Vsrc) {
- /*FIXME: This code seems weird, however, this is how it is
- working right now.
- */
- res=height/2;
- if (!is_50hz)
- res+=8;
- } else
- res=height;
-
- /* height */
- saa711x_write(client, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH,
- (u8) (res & 0xff));
- saa711x_write(client, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB,
- (u8) (res & 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(client, R_D0_B_HORIZ_PRESCALING,
- (u8) (HPSC & 0x3f));
-
- v4l_dbg(1, debug, client, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC);
- /* write H fine-scaling (luminance) */
- saa711x_write(client, R_D8_B_HORIZ_LUMA_SCALING_INC,
- (u8) (HFSC & 0xff));
- saa711x_write(client, 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,
- (u8) ((HFSC >> 1) & 0xff));
- saa711x_write(client, 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);
-
- /* Correct Contrast and Luminance */
- saa711x_write(client, R_D5_B_LUMA_CONTRAST_CNTL,
- (u8) (64 * 1024 / VSCY));
- saa711x_write(client, 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,
- (u8) (VSCY & 0xff));
- saa711x_write(client, 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,
- (u8) (VSCY & 0xff));
- saa711x_write(client, R_E3_B_VERT_CHROMA_SCALING_INC_MSB,
- (u8) ((VSCY >> 8) & 0xff));
-
- saa711x_writeregs(client, saa7115_cfg_reset_scaler);
-
- return 0;
-}
-
static int saa711x_set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
{
if (fmt->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
/* ============ SAA7115 AUDIO settings (end) ============= */
-static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *arg)
+static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
struct saa711x_state *state = i2c_get_clientdata(client);
- int *iarg = arg;
/* ioctls to allow direct access to the saa7115 registers for testing */
switch (cmd) {
struct v4l2_routing *route = arg;
route->input = state->input;
- route->output = 0;
+ route->output = state->output;
break;
}
{
struct v4l2_routing *route = arg;
- v4l_dbg(1, debug, client, "decoder set input %d\n", route->input);
+ v4l_dbg(1, debug, client, "decoder set input %d output %d\n", route->input, route->output);
/* saa7113 does not have these inputs */
if (state->ident == V4L2_IDENT_SAA7113 &&
(route->input == SAA7115_COMPOSITE4 ||
}
if (route->input > SAA7115_SVIDEO3)
return -EINVAL;
- if (state->input == route->input)
+ if (route->output > SAA7115_IPORT_ON)
+ return -EINVAL;
+ if (state->input == route->input && state->output == route->output)
break;
- v4l_dbg(1, debug, client, "now setting %s input\n",
- (route->input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite");
+ v4l_dbg(1, debug, client, "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;
/* select mode */
saa711x_write(client, R_09_LUMA_CNTL,
(saa711x_read(client, 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(client, R_83_X_PORT_I_O_ENA_AND_OUT_CLK,
+ (saa711x_read(client, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) |
+ (state->output & 0x01));
+ }
break;
}
{
struct v4l2_sliced_vbi_data *data = arg;
+ /* 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(client, 0x6b) & 0xc0)
case V4L2_SLICED_CAPTION_525:
if (data->field == 0) {
/* CC */
- if (saa711x_read(client, 0x66) & 0xc0)
+ if (saa711x_read(client, 0x66) & 0x30)
return -EIO;
- data->data[0] = saa711x_read(client, 0x67);
- data->data[1] = saa711x_read(client, 0x68);
+ data->data[0] = saa711x_read(client, 0x69);
+ data->data[1] = saa711x_read(client, 0x6a);
return 0;
}
/* XDS */
- if (saa711x_read(client, 0x66) & 0x30)
+ if (saa711x_read(client, 0x66) & 0xc0)
return -EIO;
- data->data[0] = saa711x_read(client, 0x69);
- data->data[1] = saa711x_read(client, 0x6a);
+ data->data[0] = saa711x_read(client, 0x67);
+ data->data[1] = saa711x_read(client, 0x68);
return 0;
default:
return -EINVAL;
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_INT_G_REGISTER:
- {
- struct v4l2_register *reg = arg;
-
- if (reg->i2c_id != I2C_DRIVERID_SAA711X)
- return -EINVAL;
- reg->val = saa711x_read(client, reg->reg & 0xff);
- break;
- }
-
- case VIDIOC_INT_S_REGISTER:
+ case VIDIOC_DBG_G_REGISTER:
+ case VIDIOC_DBG_S_REGISTER:
{
struct v4l2_register *reg = arg;
- if (reg->i2c_id != I2C_DRIVERID_SAA711X)
+ if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip))
return -EINVAL;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- saa711x_write(client, reg->reg & 0xff, reg->val & 0xff);
+ if (cmd == VIDIOC_DBG_G_REGISTER)
+ reg->val = saa711x_read(client, reg->reg & 0xff);
+ else
+ saa711x_write(client, reg->reg & 0xff, reg->val & 0xff);
break;
}
#endif
- case VIDIOC_INT_G_CHIP_IDENT:
- *iarg = state->ident;
- break;
+ case VIDIOC_G_CHIP_IDENT:
+ return v4l2_chip_ident_i2c_client(client, arg, state->ident, 0);
default:
return -EINVAL;
/* ----------------------------------------------------------------------- */
-static struct i2c_driver i2c_driver_saa711x;
-
-static int saa711x_attach(struct i2c_adapter *adapter, int address, int kind)
+static int saa7115_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
- struct i2c_client *client;
struct saa711x_state *state;
int i;
char name[17];
u8 chip_id;
/* Check if the adapter supports the needed features */
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
- return 0;
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
- 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++) {
+ 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;
+ name[i] = (saa711x_read(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;
- if (chip_id < 3 && chip_id > 5) {
- v4l_dbg(1, debug, client, "saa7115 not found\n");
- kfree(client);
- return 0;
+
+ /* 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;
}
+
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);
+ v4l_info(client, "saa711%d 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);
return -ENOMEM;
}
state->input = -1;
+ state->output = SAA7115_IPORT_ON;
state->enable = 1;
state->radio = 0;
state->bright = 128;
v4l_dbg(1, debug, client, "writing init values\n");
/* init to 60hz/48khz */
- if (state->ident == V4L2_IDENT_SAA7111 ||
- state->ident == V4L2_IDENT_SAA7113) {
- state->crystal_freq = SAA7115_FREQ_24_576_MHZ;
- saa711x_writeregs(client, saa7113_init_auto_input);
- } else {
+ state->crystal_freq = SAA7115_FREQ_24_576_MHZ;
+ switch (state->ident) {
+ case V4L2_IDENT_SAA7111:
+ saa711x_writeregs(client, saa7111_init);
+ break;
+ case V4L2_IDENT_SAA7113:
+ saa711x_writeregs(client, saa7113_init);
+ break;
+ default:
state->crystal_freq = SAA7115_FREQ_32_11_MHZ;
saa711x_writeregs(client, saa7115_init_auto_input);
}
saa711x_writeregs(client, saa7115_init_misc);
- state->std = V4L2_STD_NTSC;
- saa711x_set_size(client, 720, 480);
- saa711x_writeregs(client, saa7115_cfg_60hz_video);
- saa711x_set_audio_clock_freq(client, state->audclk_freq);
- saa711x_writeregs(client, saa7115_cfg_reset_scaler);
-
- i2c_attach_client(client);
+ saa711x_set_v4lstd(client, V4L2_STD_NTSC);
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));
-
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 saa7115_remove(struct i2c_client *client)
{
- struct saa711x_state *state = i2c_get_clientdata(client);
- int err;
-
- err = i2c_detach_client(client);
- if (err) {
- return err;
- }
-
- kfree(state);
- kfree(client);
+ kfree(i2c_get_clientdata(client));
return 0;
}
-/* ----------------------------------------------------------------------- */
-
-/* i2c implementation */
-static struct i2c_driver i2c_driver_saa711x = {
- .driver = {
- .name = "saa7115",
- },
- .id = I2C_DRIVERID_SAA711X,
- .attach_adapter = saa711x_probe,
- .detach_client = saa711x_detach,
- .command = saa711x_command,
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "saa7115",
+ .driverid = I2C_DRIVERID_SAA711X,
+ .command = saa7115_command,
+ .probe = saa7115_probe,
+ .remove = saa7115_remove,
+ .legacy_class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL,
};
-
-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);