summaryrefslogtreecommitdiff
path: root/drivers/media/video/cx25840/cx25840-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/cx25840/cx25840-core.c')
-rw-r--r--drivers/media/video/cx25840/cx25840-core.c185
1 files changed, 140 insertions, 45 deletions
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
index 737ee4ea8830..0be51b65f098 100644
--- a/drivers/media/video/cx25840/cx25840-core.c
+++ b/drivers/media/video/cx25840/cx25840-core.c
@@ -345,6 +345,77 @@ static void cx23885_initialize(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
+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);
+}
+
+/* ----------------------------------------------------------------------- */
+
void cx25840_std_setup(struct i2c_client *client)
{
struct cx25840_state *state = to_state(i2c_get_clientdata(client));
@@ -414,39 +485,41 @@ void cx25840_std_setup(struct i2c_client *client)
}
/* DEBUG: Displays configured PLL frequency */
- 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;
+ if (!state->is_cx231xx) {
+ 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,
- "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);
+ "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 */
@@ -596,7 +669,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
* configuration in reg (for the cx23885) so we have no
* need to attempt to flip bits for earlier av decoders.
*/
- if (!state->is_cx23885) {
+ if (!state->is_cx23885 && !state->is_cx231xx) {
switch (aud_input) {
case CX25840_AUDIO_SERIAL:
/* do nothing, use serial audio input */
@@ -619,7 +692,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
/* Set INPUT_MODE to Composite (0) or S-Video (1) */
cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02);
- if (!state->is_cx23885) {
+ if (!state->is_cx23885 && !state->is_cx231xx) {
/* 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 */
@@ -659,6 +732,19 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
*/
cx25840_write(client, 0x918, 0xa0);
cx25840_write(client, 0x919, 0x01);
+ } else if (state->is_cx231xx) {
+ /* 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);
}
return 0;
@@ -1096,7 +1182,7 @@ static void log_audio_status(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
-/* This init operation must be called to load the driver's firmware.
+/* 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.
@@ -1106,18 +1192,20 @@ static void log_audio_status(struct i2c_client *client)
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_init(struct v4l2_subdev *sd, u32 val)
+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 on first use */
+ /* initialize and load firmware */
state->is_initialized = 1;
if (state->is_cx25836)
cx25836_initialize(client);
else if (state->is_cx23885)
cx23885_initialize(client);
+ else if (state->is_cx231xx)
+ cx231xx_initialize(client);
else
cx25840_initialize(client);
}
@@ -1159,7 +1247,7 @@ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable)
v4l_dbg(1, cx25840_debug, client, "%s output\n",
enable ? "enable" : "disable");
if (enable) {
- if (state->is_cx23885) {
+ if (state->is_cx23885 || state->is_cx231xx) {
u8 v = (cx25840_read(client, 0x421) | 0x0b);
cx25840_write(client, 0x421, v);
} else {
@@ -1169,7 +1257,7 @@ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable)
state->is_cx25836 ? 0x04 : 0x07);
}
} else {
- if (state->is_cx23885) {
+ if (state->is_cx23885 || state->is_cx231xx) {
u8 v = cx25840_read(client, 0x421) & ~(0x0b);
cx25840_write(client, 0x421, v);
} else {
@@ -1234,22 +1322,24 @@ static int cx25840_s_radio(struct v4l2_subdev *sd)
return 0;
}
-static int cx25840_s_video_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+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);
- return set_input(client, route->input, state->aud_input);
+ return set_input(client, input, state->aud_input);
}
-static int cx25840_s_audio_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+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 (state->is_cx25836)
return -EINVAL;
- return set_input(client, state->vid_input, route->input);
+ return set_input(client, state->vid_input, input);
}
static int cx25840_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
@@ -1350,6 +1440,8 @@ static int cx25840_reset(struct v4l2_subdev *sd, u32 val)
cx25836_initialize(client);
else if (state->is_cx23885)
cx23885_initialize(client);
+ else if (state->is_cx231xx)
+ cx231xx_initialize(client);
else
cx25840_initialize(client);
return 0;
@@ -1382,8 +1474,9 @@ static const struct v4l2_subdev_core_ops cx25840_core_ops = {
.g_ctrl = cx25840_g_ctrl,
.s_ctrl = cx25840_s_ctrl,
.queryctrl = cx25840_queryctrl,
+ .s_std = cx25840_s_std,
.reset = cx25840_reset,
- .init = cx25840_init,
+ .load_fw = cx25840_load_fw,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = cx25840_g_register,
.s_register = cx25840_s_register,
@@ -1392,7 +1485,6 @@ static const struct v4l2_subdev_core_ops cx25840_core_ops = {
static const struct v4l2_subdev_tuner_ops cx25840_tuner_ops = {
.s_frequency = cx25840_s_frequency,
- .s_std = cx25840_s_std,
.s_radio = cx25840_s_radio,
.g_tuner = cx25840_g_tuner,
.s_tuner = cx25840_s_tuner,
@@ -1449,6 +1541,8 @@ static int cx25840_probe(struct i2c_client *client,
id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6;
} else if (device_id == 0x1313) {
id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6;
+ } else if ((device_id & 0xfff0) == 0x5A30) {
+ id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf);
}
else {
v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n");
@@ -1471,6 +1565,7 @@ static int cx25840_probe(struct i2c_client *client,
state->c = client;
state->is_cx25836 = ((device_id & 0xff00) == 0x8300);
state->is_cx23885 = (device_id == 0x0000) || (device_id == 0x1313);
+ state->is_cx231xx = (device_id == 0x5a3e);
state->vid_input = CX25840_COMPOSITE7;
state->aud_input = CX25840_AUDIO8;
state->audclk_freq = 48000;