#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv-legacy.h>
+#include <media/v4l2-i2c-drv.h>
#include <media/saa7115.h>
#include <asm/div64.h>
MODULE_PARM_DESC(debug, "Debug level (0-1)");
-static unsigned short normal_i2c[] = {
- 0x4a >> 1, 0x48 >> 1, /* SAA7111, SAA7111A and SAA7113 */
- 0x42 >> 1, 0x40 >> 1, /* SAA7114, SAA7115 and SAA7118 */
- I2C_CLIENT_END };
-
-I2C_CLIENT_INSMOD;
struct saa711x_state {
struct v4l2_subdev sd;
int contrast;
int hue;
int sat;
+ int chroma_agc;
int width;
int height;
u32 ident;
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 ||
R_5D_DID, 0xbd,
R_5E_SDID, 0x35,
- R_02_INPUT_CNTL_1, 0x84, /* input tuner -> input 4, amplifier active */
+ 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,
static int saa711x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
struct saa711x_state *state = to_state(sd);
+ u8 val;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
state->hue = ctrl->value;
saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, state->hue);
break;
-
+ case V4L2_CID_CHROMA_AGC:
+ val = saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL);
+ state->chroma_agc = ctrl->value;
+ if (ctrl->value)
+ val &= 0x7f;
+ else
+ val |= 0x80;
+ saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, val);
+ break;
+ case V4L2_CID_CHROMA_GAIN:
+ /* Chroma gain cannot be set when AGC is enabled */
+ if (state->chroma_agc == 1)
+ return -EINVAL;
+ saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, ctrl->value | 0x80);
+ break;
default:
return -EINVAL;
}
case V4L2_CID_HUE:
ctrl->value = state->hue;
break;
+ case V4L2_CID_CHROMA_AGC:
+ ctrl->value = state->chroma_agc;
+ break;
+ case V4L2_CID_CHROMA_GAIN:
+ ctrl->value = saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f;
+ break;
default:
return -EINVAL;
}
/* 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 VIDIOC_S_STD is also used to switch from
- radio to TV mode, so if a VIDIOC_S_STD is broadcast to
+ 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)
011 NTSC N (3.58MHz) PAL M (3.58MHz)
100 reserved NTSC-Japan (3.58MHz)
*/
- if (state->ident == V4L2_IDENT_SAA7111 ||
- state->ident == V4L2_IDENT_SAA7113) {
+ if (state->ident <= V4L2_IDENT_SAA7113) {
u8 reg = saa711x_read(sd, R_0E_CHROMA_CNTL_1) & 0x8f;
if (std == V4L2_STD_PAL_M) {
saa7115_cfg_vbi_off);
}
-static int saa711x_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+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 */
V4L2_SLICED_VPS, 0, 0, 0, 0, /* 7 */
0, 0, 0, 0
};
- struct v4l2_sliced_vbi_format *sliced = &fmt->fmt.sliced;
int i;
- if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
- return -EINVAL;
memset(sliced, 0, sizeof(*sliced));
/* done if using raw VBI */
if (saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10)
return 0;
}
+static int saa711x_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+{
+ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+ return -EINVAL;
+ return saa711x_g_sliced_fmt(sd, &fmt->fmt.sliced);
+}
+
+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_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
{
- if (fmt->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
- 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_SLICED_VBI_CAPTURE)
+ return saa711x_s_sliced_fmt(sd, &fmt->fmt.sliced);
+ if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ return saa711x_s_raw_fmt(sd, &fmt->fmt.vbi);
if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64);
case V4L2_CID_HUE:
return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
+ case V4L2_CID_CHROMA_AGC:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
+ case V4L2_CID_CHROMA_GAIN:
+ return v4l2_ctrl_query_fill(qc, 0, 127, 1, 48);
default:
return -EINVAL;
}
return 0;
}
-static int saa711x_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+static int saa711x_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
{
struct saa711x_state *state = to_state(sd);
- u32 input = route->input;
- u8 mask = (state->ident == V4L2_IDENT_SAA7111) ? 0xf8 : 0xf0;
+ u8 mask = (state->ident <= V4L2_IDENT_SAA7111A) ? 0xf8 : 0xf0;
+
+ v4l2_dbg(1, debug, sd, "decoder set input %d output %d\n",
+ input, output);
- 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)) {
+ if (state->ident <= V4L2_IDENT_SAA7113 &&
+ (input == SAA7115_COMPOSITE4 ||
+ input == SAA7115_COMPOSITE5)) {
return -EINVAL;
}
- if (route->input > SAA7115_SVIDEO3)
- return -EINVAL;
- if (route->output > SAA7115_IPORT_ON)
+ if (input > SAA7115_SVIDEO3)
return -EINVAL;
- if (state->input == route->input && state->output == route->output)
+ if (state->input == input && state->output == 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;
+ (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_SAA7111) {
+ 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) |
- ((route->output & 0xc0) ^ 0x40));
+ ((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));
+ ((output & 2) ? 0x0a : 0));
}
/* select mode */
(saa711x_read(sd, R_09_LUMA_CNTL) & 0x7f) |
(state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0));
- state->output = route->output;
+ 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,
{
struct saa711x_state *state = to_state(sd);
- if (state->ident != V4L2_IDENT_SAA7111)
+ 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_crystal_freq(struct v4l2_subdev *sd, struct v4l2_crystal_freq *freq)
+static int saa711x_s_crystal_freq(struct v4l2_subdev *sd, u32 freq, u32 flags)
{
struct saa711x_state *state = to_state(sd);
- if (freq->freq != SAA7115_FREQ_32_11_MHZ &&
- freq->freq != SAA7115_FREQ_24_576_MHZ)
+ if (freq != SAA7115_FREQ_32_11_MHZ && 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;
+ 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_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ struct saa711x_state *state = to_state(sd);
+ int reg1e;
+
+ *std = V4L2_STD_ALL;
+ if (state->ident != V4L2_IDENT_SAA7115)
+ return 0;
+ reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC);
+
+ switch (reg1e & 0x03) {
+ case 1:
+ *std = V4L2_STD_NTSC;
+ break;
+ case 2:
+ *std = V4L2_STD_PAL;
+ break;
+ case 3:
+ *std = V4L2_STD_SECAM;
+ break;
+ default:
+ break;
+ }
+ 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)
{
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 const struct v4l2_subdev_core_ops saa711x_core_ops = {
.g_ctrl = saa711x_g_ctrl,
.s_ctrl = saa711x_s_ctrl,
.queryctrl = saa711x_queryctrl,
+ .s_std = saa711x_s_std,
.reset = saa711x_reset,
.s_gpio = saa711x_s_gpio,
#ifdef CONFIG_VIDEO_ADV_DEBUG
};
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,
};
.s_crystal_freq = saa711x_s_crystal_freq,
.g_fmt = saa711x_g_fmt,
.s_fmt = saa711x_s_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,
- .s_stream = saa711x_s_stream,
+ .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 = {
.tuner = &saa711x_tuner_ops,
.audio = &saa711x_audio_ops,
.video = &saa711x_video_ops,
+ .vbi = &saa711x_vbi_ops,
};
/* ----------------------------------------------------------------------- */
state->contrast = 64;
state->hue = 0;
state->sat = 64;
+ state->chroma_agc = 1;
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;
default:
state->ident = V4L2_IDENT_SAA7111;
v4l2_info(sd, "WARNING: Chip is not known - Falling back to saa7111\n");
-
+ break;
}
state->audclk_freq = 48000;
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:
state->crystal_freq = SAA7115_FREQ_32_11_MHZ;
saa711x_writeregs(sd, saa7115_init_auto_input);
}
- if (state->ident != V4L2_IDENT_SAA7111)
+ if (state->ident > V4L2_IDENT_SAA7111A)
saa711x_writeregs(sd, saa7115_init_misc);
saa711x_set_v4lstd(sd, V4L2_STD_NTSC);
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,
};