aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/video/cx18
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/cx18')
-rw-r--r--drivers/media/video/cx18/cx18-av-audio.c231
-rw-r--r--drivers/media/video/cx18/cx18-av-core.c106
-rw-r--r--drivers/media/video/cx18/cx18-av-core.h5
-rw-r--r--drivers/media/video/cx18/cx18-av-firmware.c28
-rw-r--r--drivers/media/video/cx18/cx18-av-vbi.c5
-rw-r--r--drivers/media/video/cx18/cx18-cards.c9
-rw-r--r--drivers/media/video/cx18/cx18-cards.h6
-rw-r--r--drivers/media/video/cx18/cx18-controls.c5
-rw-r--r--drivers/media/video/cx18/cx18-driver.c284
-rw-r--r--drivers/media/video/cx18/cx18-driver.h78
-rw-r--r--drivers/media/video/cx18/cx18-dvb.c59
-rw-r--r--drivers/media/video/cx18/cx18-dvb.h1
-rw-r--r--drivers/media/video/cx18/cx18-fileops.c38
-rw-r--r--drivers/media/video/cx18/cx18-firmware.c229
-rw-r--r--drivers/media/video/cx18/cx18-gpio.c23
-rw-r--r--drivers/media/video/cx18/cx18-gpio.h1
-rw-r--r--drivers/media/video/cx18/cx18-i2c.c31
-rw-r--r--drivers/media/video/cx18/cx18-io.c198
-rw-r--r--drivers/media/video/cx18/cx18-io.h326
-rw-r--r--drivers/media/video/cx18/cx18-ioctl.c12
-rw-r--r--drivers/media/video/cx18/cx18-ioctl.h1
-rw-r--r--drivers/media/video/cx18/cx18-irq.c163
-rw-r--r--drivers/media/video/cx18/cx18-irq.h4
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.c527
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.h29
-rw-r--r--drivers/media/video/cx18/cx18-queue.c118
-rw-r--r--drivers/media/video/cx18/cx18-queue.h22
-rw-r--r--drivers/media/video/cx18/cx18-scb.c2
-rw-r--r--drivers/media/video/cx18/cx18-scb.h9
-rw-r--r--drivers/media/video/cx18/cx18-streams.c140
-rw-r--r--drivers/media/video/cx18/cx18-streams.h5
-rw-r--r--drivers/media/video/cx18/cx18-vbi.c5
-rw-r--r--drivers/media/video/cx18/cx18-version.h2
-rw-r--r--drivers/media/video/cx18/cx23418.h6
34 files changed, 1571 insertions, 1137 deletions
diff --git a/drivers/media/video/cx18/cx18-av-audio.c b/drivers/media/video/cx18/cx18-av-audio.c
index 0b55837880a..a2f0ad57043 100644
--- a/drivers/media/video/cx18/cx18-av-audio.c
+++ b/drivers/media/video/cx18/cx18-av-audio.c
@@ -4,6 +4,7 @@
* Derived from cx25840-audio.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -30,98 +31,165 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq)
if (freq != 32000 && freq != 44100 && freq != 48000)
return -EINVAL;
- /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */
- cx18_av_write(cx, 0x127, 0x50);
+ /*
+ * The PLL parameters are based on the external crystal frequency that
+ * would ideally be:
+ *
+ * NTSC Color subcarrier freq * 8 =
+ * 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz
+ *
+ * The accidents of history and rationale that explain from where this
+ * combination of magic numbers originate can be found in:
+ *
+ * [1] Abrahams, I. C., "Choice of Chrominance Subcarrier Frequency in
+ * the NTSC Standards", Proceedings of the I-R-E, January 1954, pp 79-80
+ *
+ * [2] Abrahams, I. C., "The 'Frequency Interleaving' Principle in the
+ * NTSC Standards", Proceedings of the I-R-E, January 1954, pp 81-83
+ *
+ * As Mike Bradley has rightly pointed out, it's not the exact crystal
+ * frequency that matters, only that all parts of the driver and
+ * firmware are using the same value (close to the ideal value).
+ *
+ * Since I have a strong suspicion that, if the firmware ever assumes a
+ * crystal value at all, it will assume 28.636360 MHz, the crystal
+ * freq used in calculations in this driver will be:
+ *
+ * xtal_freq = 28.636360 MHz
+ *
+ * an error of less than 0.13 ppm which is way, way better than any off
+ * the shelf crystal will have for accuracy anyway.
+ *
+ * Below I aim to run the PLLs' VCOs near 400 MHz to minimze error.
+ *
+ * Many thanks to Jeff Campbell and Mike Bradley for their extensive
+ * investigation, experimentation, testing, and suggested solutions of
+ * of audio/video sync problems with SVideo and CVBS captures.
+ */
if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
switch (freq) {
case 32000:
- /* VID_PLL and AUX_PLL */
- cx18_av_write4(cx, 0x108, 0x1408040f);
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x20
+ */
+ cx18_av_write4(cx, 0x108, 0x200d040f);
- /* AUX_PLL_FRAC */
- /* 0x8.9504318a * 28,636,363.636 / 0x14 = 32000 * 384 */
- cx18_av_write4(cx, 0x110, 0x012a0863);
+ /* VID_PLL Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+ cx18_av_write4(cx, 0x10c, 0x002be2fe);
+
+ /* AUX_PLL Fraction = 0x176740c */
+ /* xtal * 0xd.bb3a060/0x20 = 32000 * 384: 393 MHz p-pd*/
+ cx18_av_write4(cx, 0x110, 0x0176740c);
/* src3/4/6_ctl */
- /* 0x1.f77f = (4 * 15734.26) / 32000 */
+ /* 0x1.f77f = (4 * xtal/8*2/455) / 32000 */
cx18_av_write4(cx, 0x900, 0x0801f77f);
cx18_av_write4(cx, 0x904, 0x0801f77f);
cx18_av_write4(cx, 0x90c, 0x0801f77f);
- /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */
- cx18_av_write(cx, 0x127, 0x54);
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x20 */
+ cx18_av_write(cx, 0x127, 0x60);
/* AUD_COUNT = 0x2fff = 8 samples * 4 * 384 - 1 */
cx18_av_write4(cx, 0x12c, 0x11202fff);
/*
- * EN_AV_LOCK = 1
+ * EN_AV_LOCK = 0
* VID_COUNT = 0x0d2ef8 = 107999.000 * 8 =
* ((8 samples/32,000) * (13,500,000 * 8) * 4 - 1) * 8
*/
- cx18_av_write4(cx, 0x128, 0xa10d2ef8);
+ cx18_av_write4(cx, 0x128, 0xa00d2ef8);
break;
case 44100:
- /* VID_PLL and AUX_PLL */
- cx18_av_write4(cx, 0x108, 0x1009040f);
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x18
+ */
+ cx18_av_write4(cx, 0x108, 0x180e040f);
+
+ /* VID_PLL Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+ cx18_av_write4(cx, 0x10c, 0x002be2fe);
- /* AUX_PLL_FRAC */
- /* 0x9.7635e7 * 28,636,363.63 / 0x10 = 44100 * 384 */
- cx18_av_write4(cx, 0x110, 0x00ec6bce);
+ /* AUX_PLL Fraction = 0x062a1f2 */
+ /* xtal * 0xe.3150f90/0x18 = 44100 * 384: 406 MHz p-pd*/
+ cx18_av_write4(cx, 0x110, 0x0062a1f2);
/* src3/4/6_ctl */
- /* 0x1.6d59 = (4 * 15734.26) / 44100 */
+ /* 0x1.6d59 = (4 * xtal/8*2/455) / 44100 */
cx18_av_write4(cx, 0x900, 0x08016d59);
cx18_av_write4(cx, 0x904, 0x08016d59);
cx18_av_write4(cx, 0x90c, 0x08016d59);
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x18 */
+ cx18_av_write(cx, 0x127, 0x58);
+
/* AUD_COUNT = 0x92ff = 49 samples * 2 * 384 - 1 */
cx18_av_write4(cx, 0x12c, 0x112092ff);
/*
- * EN_AV_LOCK = 1
+ * EN_AV_LOCK = 0
* VID_COUNT = 0x1d4bf8 = 239999.000 * 8 =
* ((49 samples/44,100) * (13,500,000 * 8) * 2 - 1) * 8
*/
- cx18_av_write4(cx, 0x128, 0xa11d4bf8);
+ cx18_av_write4(cx, 0x128, 0xa01d4bf8);
break;
case 48000:
- /* VID_PLL and AUX_PLL */
- cx18_av_write4(cx, 0x108, 0x100a040f);
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x16
+ */
+ cx18_av_write4(cx, 0x108, 0x160e040f);
- /* AUX_PLL_FRAC */
- /* 0xa.4c6b6ea * 28,636,363.63 / 0x10 = 48000 * 384 */
- cx18_av_write4(cx, 0x110, 0x0098d6dd);
+ /* VID_PLL Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+ cx18_av_write4(cx, 0x10c, 0x002be2fe);
+
+ /* AUX_PLL Fraction = 0x05227ad */
+ /* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz p-pd*/
+ cx18_av_write4(cx, 0x110, 0x005227ad);
/* src3/4/6_ctl */
- /* 0x1.4faa = (4 * 15734.26) / 48000 */
+ /* 0x1.4faa = (4 * xtal/8*2/455) / 48000 */
cx18_av_write4(cx, 0x900, 0x08014faa);
cx18_av_write4(cx, 0x904, 0x08014faa);
cx18_av_write4(cx, 0x90c, 0x08014faa);
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */
+ cx18_av_write(cx, 0x127, 0x56);
+
/* AUD_COUNT = 0x5fff = 4 samples * 16 * 384 - 1 */
cx18_av_write4(cx, 0x12c, 0x11205fff);
/*
- * EN_AV_LOCK = 1
+ * EN_AV_LOCK = 0
* VID_COUNT = 0x1193f8 = 143999.000 * 8 =
* ((4 samples/48,000) * (13,500,000 * 8) * 16 - 1) * 8
*/
- cx18_av_write4(cx, 0x128, 0xa11193f8);
+ cx18_av_write4(cx, 0x128, 0xa01193f8);
break;
}
} else {
switch (freq) {
case 32000:
- /* VID_PLL and AUX_PLL */
- cx18_av_write4(cx, 0x108, 0x1e08040f);
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x30
+ */
+ cx18_av_write4(cx, 0x108, 0x300d040f);
+
+ /* VID_PLL Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+ cx18_av_write4(cx, 0x10c, 0x002be2fe);
- /* AUX_PLL_FRAC */
- /* 0x8.9504318 * 28,636,363.63 / 0x1e = 32000 * 256 */
- cx18_av_write4(cx, 0x110, 0x012a0863);
+ /* AUX_PLL Fraction = 0x176740c */
+ /* xtal * 0xd.bb3a060/0x30 = 32000 * 256: 393 MHz p-pd*/
+ cx18_av_write4(cx, 0x110, 0x0176740c);
/* src1_ctl */
/* 0x1.0000 = 32000/32000 */
@@ -133,27 +201,34 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq)
cx18_av_write4(cx, 0x904, 0x08020000);
cx18_av_write4(cx, 0x90c, 0x08020000);
- /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */
- cx18_av_write(cx, 0x127, 0x54);
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x30 */
+ cx18_av_write(cx, 0x127, 0x70);
/* AUD_COUNT = 0x1fff = 8 samples * 4 * 256 - 1 */
cx18_av_write4(cx, 0x12c, 0x11201fff);
/*
- * EN_AV_LOCK = 1
+ * EN_AV_LOCK = 0
* VID_COUNT = 0x0d2ef8 = 107999.000 * 8 =
* ((8 samples/32,000) * (13,500,000 * 8) * 4 - 1) * 8
*/
- cx18_av_write4(cx, 0x128, 0xa10d2ef8);
+ cx18_av_write4(cx, 0x128, 0xa00d2ef8);
break;
case 44100:
- /* VID_PLL and AUX_PLL */
- cx18_av_write4(cx, 0x108, 0x1809040f);
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x24
+ */
+ cx18_av_write4(cx, 0x108, 0x240e040f);
- /* AUX_PLL_FRAC */
- /* 0x9.7635e74 * 28,636,363.63 / 0x18 = 44100 * 256 */
- cx18_av_write4(cx, 0x110, 0x00ec6bce);
+ /* VID_PLL Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+ cx18_av_write4(cx, 0x10c, 0x002be2fe);
+
+ /* AUX_PLL Fraction = 0x062a1f2 */
+ /* xtal * 0xe.3150f90/0x24 = 44100 * 256: 406 MHz p-pd*/
+ cx18_av_write4(cx, 0x110, 0x0062a1f2);
/* src1_ctl */
/* 0x1.60cd = 44100/32000 */
@@ -165,24 +240,34 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq)
cx18_av_write4(cx, 0x904, 0x08017385);
cx18_av_write4(cx, 0x90c, 0x08017385);
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x24 */
+ cx18_av_write(cx, 0x127, 0x64);
+
/* AUD_COUNT = 0x61ff = 49 samples * 2 * 256 - 1 */
cx18_av_write4(cx, 0x12c, 0x112061ff);
/*
- * EN_AV_LOCK = 1
+ * EN_AV_LOCK = 0
* VID_COUNT = 0x1d4bf8 = 239999.000 * 8 =
* ((49 samples/44,100) * (13,500,000 * 8) * 2 - 1) * 8
*/
- cx18_av_write4(cx, 0x128, 0xa11d4bf8);
+ cx18_av_write4(cx, 0x128, 0xa01d4bf8);
break;
case 48000:
- /* VID_PLL and AUX_PLL */
- cx18_av_write4(cx, 0x108, 0x180a040f);
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x20
+ */
+ cx18_av_write4(cx, 0x108, 0x200d040f);
+
+ /* VID_PLL Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+ cx18_av_write4(cx, 0x10c, 0x002be2fe);
- /* AUX_PLL_FRAC */
- /* 0xa.4c6b6ea * 28,636,363.63 / 0x18 = 48000 * 256 */
- cx18_av_write4(cx, 0x110, 0x0098d6dd);
+ /* AUX_PLL Fraction = 0x176740c */
+ /* xtal * 0xd.bb3a060/0x20 = 48000 * 256: 393 MHz p-pd*/
+ cx18_av_write4(cx, 0x110, 0x0176740c);
/* src1_ctl */
/* 0x1.8000 = 48000/32000 */
@@ -194,15 +279,18 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq)
cx18_av_write4(cx, 0x904, 0x08015555);
cx18_av_write4(cx, 0x90c, 0x08015555);
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x20 */
+ cx18_av_write(cx, 0x127, 0x60);
+
/* AUD_COUNT = 0x3fff = 4 samples * 16 * 256 - 1 */
cx18_av_write4(cx, 0x12c, 0x11203fff);
/*
- * EN_AV_LOCK = 1
+ * EN_AV_LOCK = 0
* VID_COUNT = 0x1193f8 = 143999.000 * 8 =
* ((4 samples/48,000) * (13,500,000 * 8) * 16 - 1) * 8
*/
- cx18_av_write4(cx, 0x128, 0xa11193f8);
+ cx18_av_write4(cx, 0x128, 0xa01193f8);
break;
}
}
@@ -215,12 +303,15 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq)
void cx18_av_audio_set_path(struct cx18 *cx)
{
struct cx18_av_state *state = &cx->av_state;
+ u8 v;
/* stop microcontroller */
- cx18_av_and_or(cx, 0x803, ~0x10, 0);
+ v = cx18_av_read(cx, 0x803) & ~0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
/* assert soft reset */
- cx18_av_and_or(cx, 0x810, ~0x1, 0x01);
+ v = cx18_av_read(cx, 0x810) | 0x01;
+ cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
/* Mute everything to prevent the PFFT! */
cx18_av_write(cx, 0x8d3, 0x1f);
@@ -240,12 +331,14 @@ void cx18_av_audio_set_path(struct cx18 *cx)
set_audclk_freq(cx, state->audclk_freq);
/* deassert soft reset */
- cx18_av_and_or(cx, 0x810, ~0x1, 0x00);
+ v = cx18_av_read(cx, 0x810) & ~0x01;
+ cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
/* When the microcontroller detects the
* audio format, it will unmute the lines */
- cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
+ v = cx18_av_read(cx, 0x803) | 0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
}
}
@@ -347,19 +440,23 @@ static int get_mute(struct cx18 *cx)
static void set_mute(struct cx18 *cx, int mute)
{
struct cx18_av_state *state = &cx->av_state;
+ u8 v;
if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
/* Must turn off microcontroller in order to mute sound.
* Not sure if this is the best method, but it does work.
* If the microcontroller is running, then it will undo any
* changes to the mute register. */
+ v = cx18_av_read(cx, 0x803);
if (mute) {
/* disable microcontroller */
- cx18_av_and_or(cx, 0x803, ~0x10, 0x00);
+ v &= ~0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
cx18_av_write(cx, 0x8d3, 0x1f);
} else {
/* enable microcontroller */
- cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
+ v |= 0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
}
} else {
/* SRC1_MUTE_EN */
@@ -375,16 +472,26 @@ int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg)
switch (cmd) {
case VIDIOC_INT_AUDIO_CLOCK_FREQ:
+ {
+ u8 v;
if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
- cx18_av_and_or(cx, 0x803, ~0x10, 0);
+ v = cx18_av_read(cx, 0x803) & ~0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
cx18_av_write(cx, 0x8d3, 0x1f);
}
- cx18_av_and_or(cx, 0x810, ~0x1, 1);
+ v = cx18_av_read(cx, 0x810) | 0x1;
+ cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
+
retval = set_audclk_freq(cx, *(u32 *)arg);
- cx18_av_and_or(cx, 0x810, ~0x1, 0);
- if (state->aud_input > CX18_AV_AUDIO_SERIAL2)
- cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
+
+ v = cx18_av_read(cx, 0x810) & ~0x1;
+ cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
+ if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
+ v = cx18_av_read(cx, 0x803) | 0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
+ }
return retval;
+ }
case VIDIOC_G_CTRL:
switch (ctrl->id) {
diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c
index 73f5141a42d..0b1c84b4ddd 100644
--- a/drivers/media/video/cx18/cx18-av-core.c
+++ b/drivers/media/video/cx18/cx18-av-core.c
@@ -4,6 +4,7 @@
* Derived from cx25840-core.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -36,12 +37,31 @@ int cx18_av_write(struct cx18 *cx, u16 addr, u8 value)
return 0;
}
+int cx18_av_write_expect(struct cx18 *cx, u16 addr, u8 value, u8 eval, u8 mask)
+{
+ u32 reg = 0xc40000 + (addr & ~3);
+ int shift = (addr & 3) * 8;
+ u32 x = cx18_read_reg(cx, reg);
+
+ x = (x & ~((u32)0xff << shift)) | ((u32)value << shift);
+ cx18_write_reg_expect(cx, x, reg,
+ ((u32)eval << shift), ((u32)mask << shift));
+ return 0;
+}
+
int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value)
{
cx18_write_reg(cx, value, 0xc40000 + addr);
return 0;
}
+int
+cx18_av_write4_expect(struct cx18 *cx, u16 addr, u32 value, u32 eval, u32 mask)
+{
+ cx18_write_reg_expect(cx, value, 0xc40000 + addr, eval, mask);
+ return 0;
+}
+
int cx18_av_write4_noretry(struct cx18 *cx, u16 addr, u32 value)
{
cx18_write_reg_noretry(cx, value, 0xc40000 + addr);
@@ -61,11 +81,6 @@ u32 cx18_av_read4(struct cx18 *cx, u16 addr)
return cx18_read_reg(cx, 0xc40000 + addr);
}
-u32 cx18_av_read4_noretry(struct cx18 *cx, u16 addr)
-{
- return cx18_read_reg_noretry(cx, 0xc40000 + addr);
-}
-
int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned and_mask,
u8 or_value)
{
@@ -98,14 +113,16 @@ static void cx18_av_initialize(struct cx18 *cx)
cx18_av_loadfw(cx);
/* Stop 8051 code execution */
- cx18_av_write4(cx, CXADEC_DL_CTL, 0x03000000);
+ cx18_av_write4_expect(cx, CXADEC_DL_CTL, 0x03000000,
+ 0x03000000, 0x13000000);
/* initallize the PLL by toggling sleep bit */
v = cx18_av_read4(cx, CXADEC_HOST_REG1);
- /* enable sleep mode */
- cx18_av_write4(cx, CXADEC_HOST_REG1, v | 1);
+ /* enable sleep mode - register appears to be read only... */
+ cx18_av_write4_expect(cx, CXADEC_HOST_REG1, v | 1, v, 0xfffe);
/* disable sleep mode */
- cx18_av_write4(cx, CXADEC_HOST_REG1, v & 0xfffe);
+ cx18_av_write4_expect(cx, CXADEC_HOST_REG1, v & 0xfffe,
+ v & 0xfffe, 0xffff);
/* initialize DLLs */
v = cx18_av_read4(cx, CXADEC_DLL1_DIAG_CTRL) & 0xE1FFFEFF;
@@ -125,9 +142,10 @@ static void cx18_av_initialize(struct cx18 *cx)
v = cx18_av_read4(cx, CXADEC_AFE_DIAG_CTRL3) | 1;
/* enable TUNE_FIL_RST */
- cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL3, v);
+ cx18_av_write4_expect(cx, CXADEC_AFE_DIAG_CTRL3, v, v, 0x03009F0F);
/* disable TUNE_FIL_RST */
- cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL3, v & 0xFFFFFFFE);
+ cx18_av_write4_expect(cx, CXADEC_AFE_DIAG_CTRL3,
+ v & 0xFFFFFFFE, v & 0xFFFFFFFE, 0x03009F0F);
/* enable 656 output */
cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x040C00);
@@ -251,10 +269,9 @@ void cx18_av_std_setup(struct cx18 *cx)
pll_int, pll_frac, pll_post);
if (pll_post) {
- int fin, fsc;
- int pll = 28636363L * ((((u64)pll_int) << 25) + pll_frac);
+ int fin, fsc, pll;
- pll >>= 25;
+ pll = (28636360L * ((((u64)pll_int) << 25) + pll_frac)) >> 25;
pll /= pll_post;
CX18_DEBUG_INFO("PLL = %d.%06d MHz\n",
pll / 1000000, pll % 1000000);
@@ -324,6 +341,7 @@ static void input_change(struct cx18 *cx)
{
struct cx18_av_state *state = &cx->av_state;
v4l2_std_id std = state->std;
+ u8 v;
/* Follow step 8c and 8d of section 3.16 in the cx18_av datasheet */
cx18_av_write(cx, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11);
@@ -333,31 +351,34 @@ static void input_change(struct cx18 *cx)
if (std & V4L2_STD_525_60) {
if (std == V4L2_STD_NTSC_M_JP) {
/* Japan uses EIAJ audio standard */
- cx18_av_write(cx, 0x808, 0xf7);
- cx18_av_write(cx, 0x80b, 0x02);
+ cx18_av_write_expect(cx, 0x808, 0xf7, 0xf7, 0xff);
+ cx18_av_write_expect(cx, 0x80b, 0x02, 0x02, 0x3f);
} else if (std == V4L2_STD_NTSC_M_KR) {
/* South Korea uses A2 audio standard */
- cx18_av_write(cx, 0x808, 0xf8);
- cx18_av_write(cx, 0x80b, 0x03);
+ cx18_av_write_expect(cx, 0x808, 0xf8, 0xf8, 0xff);
+ cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f);
} else {
/* Others use the BTSC audio standard */
- cx18_av_write(cx, 0x808, 0xf6);
- cx18_av_write(cx, 0x80b, 0x01);
+ cx18_av_write_expect(cx, 0x808, 0xf6, 0xf6, 0xff);
+ cx18_av_write_expect(cx, 0x80b, 0x01, 0x01, 0x3f);
}
} else if (std & V4L2_STD_PAL) {
/* Follow tuner change procedure for PAL */
- cx18_av_write(cx, 0x808, 0xff);
- cx18_av_write(cx, 0x80b, 0x03);
+ cx18_av_write_expect(cx, 0x808, 0xff, 0xff, 0xff);
+ cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f);
} else if (std & V4L2_STD_SECAM) {
/* Select autodetect for SECAM */
- cx18_av_write(cx, 0x808, 0xff);
- cx18_av_write(cx, 0x80b, 0x03);
+ cx18_av_write_expect(cx, 0x808, 0xff, 0xff, 0xff);
+ cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f);
}
- if (cx18_av_read(cx, 0x803) & 0x10) {
+ v = cx18_av_read(cx, 0x803);
+ if (v & 0x10) {
/* restart audio decoder microcontroller */
- cx18_av_and_or(cx, 0x803, ~0x10, 0x00);
- cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
+ v &= ~0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
+ v |= 0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
}
}
@@ -368,6 +389,7 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
u8 is_composite = (vid_input >= CX18_AV_COMPOSITE1 &&
vid_input <= CX18_AV_COMPOSITE8);
u8 reg;
+ u8 v;
CX18_DEBUG_INFO("decoder set video input %d, audio input %d\n",
vid_input, aud_input);
@@ -413,16 +435,23 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
return -EINVAL;
}
- cx18_av_write(cx, 0x103, reg);
+ cx18_av_write_expect(cx, 0x103, reg, reg, 0xf7);
/* Set INPUT_MODE to Composite (0) or S-Video (1) */
cx18_av_and_or(cx, 0x401, ~0x6, is_composite ? 0 : 0x02);
+
/* Set CH_SEL_ADC2 to 1 if input comes from CH3 */
- cx18_av_and_or(cx, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0);
+ v = cx18_av_read(cx, 0x102);
+ if (reg & 0x80)
+ v &= ~0x2;
+ else
+ v |= 0x2;
/* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */
if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30)
- cx18_av_and_or(cx, 0x102, ~0x4, 4);
+ v |= 0x4;
else
- cx18_av_and_or(cx, 0x102, ~0x4, 0);
+ v &= ~0x4;
+ cx18_av_write_expect(cx, 0x102, v, v, 0x17);
+
/*cx18_av_and_or4(cx, 0x104, ~0x001b4180, 0x00004180);*/
state->vid_input = vid_input;
@@ -799,40 +828,47 @@ int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg)
}
case VIDIOC_S_TUNER:
+ {
+ u8 v;
+
if (state->radio)
break;
+ v = cx18_av_read(cx, 0x809);
+ v &= ~0xf;
+
switch (vt->audmode) {
case V4L2_TUNER_MODE_MONO:
/* mono -> mono
stereo -> mono
bilingual -> lang1 */
- cx18_av_and_or(cx, 0x809, ~0xf, 0x00);
break;
case V4L2_TUNER_MODE_STEREO:
case V4L2_TUNER_MODE_LANG1:
/* mono -> mono
stereo -> stereo
bilingual -> lang1 */
- cx18_av_and_or(cx, 0x809, ~0xf, 0x04);
+ v |= 0x4;
break;
case V4L2_TUNER_MODE_LANG1_LANG2:
/* mono -> mono
stereo -> stereo
bilingual -> lang1/lang2 */
- cx18_av_and_or(cx, 0x809, ~0xf, 0x07);
+ v |= 0x7;
break;
case V4L2_TUNER_MODE_LANG2:
/* mono -> mono
stereo -> stereo
bilingual -> lang2 */
- cx18_av_and_or(cx, 0x809, ~0xf, 0x01);
+ v |= 0x1;
break;
default:
return -EINVAL;
}
+ cx18_av_write_expect(cx, 0x809, v, v, 0xff);
state->audmode = vt->audmode;
break;
+ }
case VIDIOC_G_FMT:
return get_v4lfmt(cx, (struct v4l2_format *)arg);
diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h
index b67d8df20cc..cf68a603909 100644
--- a/drivers/media/video/cx18/cx18-av-core.h
+++ b/drivers/media/video/cx18/cx18-av-core.h
@@ -4,6 +4,7 @@
* Derived from cx25840-core.h
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -302,9 +303,11 @@ struct cx18_av_state {
int cx18_av_write(struct cx18 *cx, u16 addr, u8 value);
int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value);
int cx18_av_write4_noretry(struct cx18 *cx, u16 addr, u32 value);
+int cx18_av_write_expect(struct cx18 *cx, u16 addr, u8 value, u8 eval, u8 mask);
+int cx18_av_write4_expect(struct cx18 *cx, u16 addr, u32 value, u32 eval,
+ u32 mask);
u8 cx18_av_read(struct cx18 *cx, u16 addr);
u32 cx18_av_read4(struct cx18 *cx, u16 addr);
-u32 cx18_av_read4_noretry(struct cx18 *cx, u16 addr);
int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned mask, u8 value);
int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 mask, u32 value);
int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg);
diff --git a/drivers/media/video/cx18/cx18-av-firmware.c b/drivers/media/video/cx18/cx18-av-firmware.c
index 522a035b2e8..c64fd0a05a9 100644
--- a/drivers/media/video/cx18/cx18-av-firmware.c
+++ b/drivers/media/video/cx18/cx18-av-firmware.c
@@ -2,6 +2,7 @@
* cx18 ADEC firmware functions
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -43,11 +44,13 @@ int cx18_av_loadfw(struct cx18 *cx)
/* The firmware load often has byte errors, so allow for several
retries, both at byte level and at the firmware load level. */
while (retries1 < 5) {
- cx18_av_write4(cx, CXADEC_CHIP_CTRL, 0x00010000);
- cx18_av_write(cx, CXADEC_STD_DET_CTL, 0xf6);
+ cx18_av_write4_expect(cx, CXADEC_CHIP_CTRL, 0x00010000,
+ 0x00008430, 0xffffffff); /* cx25843 */
+ cx18_av_write_expect(cx, CXADEC_STD_DET_CTL, 0xf6, 0xf6, 0xff);
- /* Reset the Mako core (Register is undocumented.) */
- cx18_av_write4(cx, 0x8100, 0x00010000);
+ /* Reset the Mako core, Register is alias of CXADEC_CHIP_CTRL */
+ cx18_av_write4_expect(cx, 0x8100, 0x00010000,
+ 0x00008430, 0xffffffff); /* cx25843 */
/* Put the 8051 in reset and enable firmware upload */
cx18_av_write4_noretry(cx, CXADEC_DL_CTL, 0x0F000000);
@@ -61,13 +64,12 @@ int cx18_av_loadfw(struct cx18 *cx)
int retries2;
int unrec_err = 0;
- for (retries2 = 0; retries2 < CX18_MAX_MMIO_RETRIES;
+ for (retries2 = 0; retries2 < CX18_MAX_MMIO_WR_RETRIES;
retries2++) {
cx18_av_write4_noretry(cx, CXADEC_DL_CTL,
dl_control);
udelay(10);
- value = cx18_av_read4_noretry(cx,
- CXADEC_DL_CTL);
+ value = cx18_av_read4(cx, CXADEC_DL_CTL);
if (value == dl_control)
break;
/* Check if we can correct the byte by changing
@@ -78,9 +80,7 @@ int cx18_av_loadfw(struct cx18 *cx)
break;
}
}
- cx18_log_write_retries(cx, retries2,
- cx->reg_mem + 0xc40000 + CXADEC_DL_CTL);
- if (unrec_err || retries2 >= CX18_MAX_MMIO_RETRIES)
+ if (unrec_err || retries2 >= CX18_MAX_MMIO_WR_RETRIES)
break;
}
if (i == size)
@@ -93,7 +93,8 @@ int cx18_av_loadfw(struct cx18 *cx)
return -EIO;
}
- cx18_av_write4(cx, CXADEC_DL_CTL, 0x13000000 | fw->size);
+ cx18_av_write4_expect(cx, CXADEC_DL_CTL,
+ 0x13000000 | fw->size, 0x13000000, 0x13000000);
/* Output to the 416 */
cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x78000);
@@ -118,7 +119,8 @@ int cx18_av_loadfw(struct cx18 *cx)
passthrough */
cx18_av_write4(cx, CXADEC_PIN_CFG3, 0x5000B687);
- cx18_av_write4(cx, CXADEC_STD_DET_CTL, 0x000000F6);
+ cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, 0x000000F6, 0x000000F6,
+ 0x3F00FFFF);
/* CxDevWrReg(CXADEC_STD_DET_CTL, 0x000000FF); */
/* Set bit 0 in register 0x9CC to signify that this is MiniMe. */
@@ -136,7 +138,7 @@ int cx18_av_loadfw(struct cx18 *cx)
v |= 0xFF; /* Auto by default */
v |= 0x400; /* Stereo by default */
v |= 0x14000000;
- cx18_av_write4(cx, CXADEC_STD_DET_CTL, v);
+ cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, v, v, 0x3F00FFFF);
release_firmware(fw);
diff --git a/drivers/media/video/cx18/cx18-av-vbi.c b/drivers/media/video/cx18/cx18-av-vbi.c
index 02fdf57bb67..1527ea4f6b0 100644
--- a/drivers/media/video/cx18/cx18-av-vbi.c
+++ b/drivers/media/video/cx18/cx18-av-vbi.c
@@ -141,10 +141,11 @@ int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg)
u8 lcr[24];
fmt = arg;
- if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE &&
+ fmt->type != V4L2_BUF_TYPE_VBI_CAPTURE)
return -EINVAL;
svbi = &fmt->fmt.sliced;
- if (svbi->service_set == 0) {
+ if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
/* raw VBI */
memset(svbi, 0, sizeof(*svbi));
diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c
index 5efe01ebe9d..e274043657d 100644
--- a/drivers/media/video/cx18/cx18-cards.c
+++ b/drivers/media/video/cx18/cx18-cards.c
@@ -4,6 +4,7 @@
* Derived from ivtv-cards.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* 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
@@ -50,7 +51,7 @@ static struct cx18_card_tuner_i2c cx18_i2c_std = {
static const struct cx18_card cx18_card_hvr1600_esmt = {
.type = CX18_CARD_HVR_1600_ESMT,
.name = "Hauppauge HVR-1600",
- .comment = "VBI is not yet supported\n",
+ .comment = "Raw VBI supported; Sliced VBI is not yet supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_muxer = CX18_HW_CS5345,
@@ -96,7 +97,7 @@ static const struct cx18_card cx18_card_hvr1600_esmt = {
static const struct cx18_card cx18_card_hvr1600_samsung = {
.type = CX18_CARD_HVR_1600_SAMSUNG,
.name = "Hauppauge HVR-1600 (Preproduction)",
- .comment = "VBI is not yet supported\n",
+ .comment = "Raw VBI supported; Sliced VBI is not yet supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_muxer = CX18_HW_CS5345,
@@ -151,7 +152,7 @@ static const struct cx18_card_pci_info cx18_pci_h900[] = {
static const struct cx18_card cx18_card_h900 = {
.type = CX18_CARD_COMPRO_H900,
.name = "Compro VideoMate H900",
- .comment = "VBI is not yet supported\n",
+ .comment = "Raw VBI supported; Sliced VBI is not yet supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_all = CX18_HW_TUNER,
@@ -248,7 +249,7 @@ static const struct cx18_card_pci_info cx18_pci_cnxt_raptor_pal[] = {
static const struct cx18_card cx18_card_cnxt_raptor_pal = {
.type = CX18_CARD_CNXT_RAPTOR_PAL,
.name = "Conexant Raptor PAL/SECAM",
- .comment = "VBI is not yet supported\n",
+ .comment = "Raw VBI supported; Sliced VBI is not yet supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_muxer = CX18_HW_GPIO,
diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h
index 32155f6e6fe..6fa7bcb42dd 100644
--- a/drivers/media/video/cx18/cx18-cards.h
+++ b/drivers/media/video/cx18/cx18-cards.h
@@ -4,6 +4,7 @@
* Derived from ivtv-cards.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* 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
@@ -47,8 +48,9 @@
/* V4L2 capability aliases */
#define CX18_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \
- V4L2_CAP_AUDIO | V4L2_CAP_READWRITE)
-/* | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE) not yet */
+ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | \
+ V4L2_CAP_VBI_CAPTURE)
+/* | V4L2_CAP_SLICED_VBI_CAPTURE) not yet */
struct cx18_card_video_input {
u8 video_type; /* video input type */
diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c
index f46c7e5ed74..17edf305d64 100644
--- a/drivers/media/video/cx18/cx18-controls.c
+++ b/drivers/media/video/cx18/cx18-controls.c
@@ -259,6 +259,7 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
return err;
}
if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+ struct cx18_api_func_private priv;
struct cx2341x_mpeg_params p = cx->params;
int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->ana_capturing),
c, VIDIOC_S_EXT_CTRLS);
@@ -278,7 +279,9 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
fmt.fmt.pix.height = cx->params.height;
cx18_av_cmd(cx, VIDIOC_S_FMT, &fmt);
}
- err = cx2341x_update(cx, cx18_api_func, &cx->params, &p);
+ priv.cx = cx;
+ priv.s = &cx->streams[id->type];
+ err = cx2341x_update(&priv, cx18_api_func, &cx->params, &p);
if (!err && cx->params.stream_vbi_fmt != p.stream_vbi_fmt)
err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt);
cx->params = p;
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
index 7874d9790a5..f50cf2167ad 100644
--- a/drivers/media/video/cx18/cx18-driver.c
+++ b/drivers/media/video/cx18/cx18-driver.c
@@ -75,48 +75,76 @@ static int radio[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1 };
-static int mmio_ndelay[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1 };
static unsigned cardtype_c = 1;
static unsigned tuner_c = 1;
static unsigned radio_c = 1;
-static unsigned mmio_ndelay_c = 1;
static char pal[] = "--";
static char secam[] = "--";
static char ntsc[] = "-";
/* Buffers */
-static int enc_mpg_buffers = CX18_DEFAULT_ENC_MPG_BUFFERS;
static int enc_ts_buffers = CX18_DEFAULT_ENC_TS_BUFFERS;
+static int enc_mpg_buffers = CX18_DEFAULT_ENC_MPG_BUFFERS;
+static int enc_idx_buffers = CX18_DEFAULT_ENC_IDX_BUFFERS;
static int enc_yuv_buffers = CX18_DEFAULT_ENC_YUV_BUFFERS;
static int enc_vbi_buffers = CX18_DEFAULT_ENC_VBI_BUFFERS;
static int enc_pcm_buffers = CX18_DEFAULT_ENC_PCM_BUFFERS;
+static int enc_ts_bufsize = CX18_DEFAULT_ENC_TS_BUFSIZE;
+static int enc_mpg_bufsize = CX18_DEFAULT_ENC_MPG_BUFSIZE;
+static int enc_idx_bufsize = CX18_DEFAULT_ENC_IDX_BUFSIZE;
+static int enc_yuv_bufsize = CX18_DEFAULT_ENC_YUV_BUFSIZE;
+/* VBI bufsize based on standards supported by card tuner for now */
+static int enc_pcm_bufsize = CX18_DEFAULT_ENC_PCM_BUFSIZE;
+
+static int enc_ts_bufs = -1;
+static int enc_mpg_bufs = -1;
+static int enc_idx_bufs = -1;
+static int enc_yuv_bufs = -1;
+static int enc_vbi_bufs = -1;
+static int enc_pcm_bufs = -1;
+
+
static int cx18_pci_latency = 1;
-int cx18_retry_mmio = 1;
+static int mmio_ndelay;
+static int retry_mmio = 1;
+
int cx18_debug;
module_param_array(tuner, int, &tuner_c, 0644);
module_param_array(radio, bool, &radio_c, 0644);
module_param_array(cardtype, int, &cardtype_c, 0644);
-module_param_array(mmio_ndelay, int, &mmio_ndelay_c, 0644);
module_param_string(pal, pal, sizeof(pal), 0644);
module_param_string(secam, secam, sizeof(secam), 0644);
module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
module_param_named(debug, cx18_debug, int, 0644);
-module_param_named(retry_mmio, cx18_retry_mmio, int, 0644);
+module_param(mmio_ndelay, int, 0644);
+module_param(retry_mmio, int, 0644);
module_param(cx18_pci_latency, int, 0644);
module_param(cx18_first_minor, int, 0644);
-module_param(enc_mpg_buffers, int, 0644);
module_param(enc_ts_buffers, int, 0644);
+module_param(enc_mpg_buffers, int, 0644);
+module_param(enc_idx_buffers, int, 0644);
module_param(enc_yuv_buffers, int, 0644);
module_param(enc_vbi_buffers, int, 0644);
module_param(enc_pcm_buffers, int, 0644);
+module_param(enc_ts_bufsize, int, 0644);
+module_param(enc_mpg_bufsize, int, 0644);
+module_param(enc_idx_bufsize, int, 0644);
+module_param(enc_yuv_bufsize, int, 0644);
+/* VBI bufsize based on standards supported by card tuner for now */
+module_param(enc_pcm_bufsize, int, 0644);
+
+module_param(enc_ts_bufs, int, 0644);
+module_param(enc_mpg_bufs, int, 0644);
+module_param(enc_idx_bufs, int, 0644);
+module_param(enc_yuv_bufs, int, 0644);
+module_param(enc_vbi_bufs, int, 0644);
+module_param(enc_pcm_bufs, int, 0644);
+
MODULE_PARM_DESC(tuner, "Tuner type selection,\n"
"\t\t\tsee tuner.h for values");
MODULE_PARM_DESC(radio,
@@ -152,28 +180,62 @@ MODULE_PARM_DESC(cx18_pci_latency,
"Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n"
"\t\t\tDefault: Yes");
MODULE_PARM_DESC(retry_mmio,
- "Check and retry memory mapped IO accesses\n"
- "\t\t\tDefault: 1 [Yes]");
+ "(Deprecated) MMIO writes are now always checked and retried\n"
+ "\t\t\tEffectively: 1 [Yes]");
MODULE_PARM_DESC(mmio_ndelay,
- "Delay (ns) for each CX23418 memory mapped IO access.\n"
- "\t\t\tTry larger values that are close to a multiple of the\n"
- "\t\t\tPCI clock period, 30.3 ns, if your card doesn't work.\n"
- "\t\t\tDefault: " __stringify(CX18_DEFAULT_MMIO_NDELAY));
-MODULE_PARM_DESC(enc_mpg_buffers,
- "Encoder MPG Buffers (in MB)\n"
- "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS));
+ "(Deprecated) MMIO accesses are now never purposely delayed\n"
+ "\t\t\tEffectively: 0 ns");
MODULE_PARM_DESC(enc_ts_buffers,
- "Encoder TS Buffers (in MB)\n"
+ "Encoder TS buffer memory (MB). (enc_ts_bufs can override)\n"
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFFERS));
+MODULE_PARM_DESC(enc_ts_bufsize,
+ "Size of an encoder TS buffer (kB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFSIZE));
+MODULE_PARM_DESC(enc_ts_bufs,
+ "Number of encoder TS buffers\n"
+ "\t\t\tDefault is computed from other enc_ts_* parameters");
+MODULE_PARM_DESC(enc_mpg_buffers,
+ "Encoder MPG buffer memory (MB). (enc_mpg_bufs can override)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS));
+MODULE_PARM_DESC(enc_mpg_bufsize,
+ "Size of an encoder MPG buffer (kB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFSIZE));
+MODULE_PARM_DESC(enc_mpg_bufs,
+ "Number of encoder MPG buffers\n"
+ "\t\t\tDefault is computed from other enc_mpg_* parameters");
+MODULE_PARM_DESC(enc_idx_buffers,
+ "Encoder IDX buffer memory (MB). (enc_idx_bufs can override)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_IDX_BUFFERS));
+MODULE_PARM_DESC(enc_idx_bufsize,
+ "Size of an encoder IDX buffer (kB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_IDX_BUFSIZE));
+MODULE_PARM_DESC(enc_idx_bufs,
+ "Number of encoder IDX buffers\n"
+ "\t\t\tDefault is computed from other enc_idx_* parameters");
MODULE_PARM_DESC(enc_yuv_buffers,
- "Encoder YUV Buffers (in MB)\n"
+ "Encoder YUV buffer memory (MB). (enc_yuv_bufs can override)\n"
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS));
+MODULE_PARM_DESC(enc_yuv_bufsize,
+ "Size of an encoder YUV buffer (kB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFSIZE));
+MODULE_PARM_DESC(enc_yuv_bufs,
+ "Number of encoder YUV buffers\n"
+ "\t\t\tDefault is computed from other enc_yuv_* parameters");
MODULE_PARM_DESC(enc_vbi_buffers,
- "Encoder VBI Buffers (in MB)\n"
+ "Encoder VBI buffer memory (MB). (enc_vbi_bufs can override)\n"
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_VBI_BUFFERS));
+MODULE_PARM_DESC(enc_vbi_bufs,
+ "Number of encoder VBI buffers\n"
+ "\t\t\tDefault is computed from enc_vbi_buffers & tuner std");
MODULE_PARM_DESC(enc_pcm_buffers,
- "Encoder PCM buffers (in MB)\n"
+ "Encoder PCM buffer memory (MB). (enc_pcm_bufs can override)\n"
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS));
+MODULE_PARM_DESC(enc_pcm_bufsize,
+ "Size of an encoder PCM buffer (kB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFSIZE));
+MODULE_PARM_DESC(enc_pcm_bufs,
+ "Number of encoder PCM buffers\n"
+ "\t\t\tDefault is computed from other enc_pcm_* parameters");
MODULE_PARM_DESC(cx18_first_minor, "Set kernel number assigned to first card");
@@ -187,7 +249,7 @@ MODULE_VERSION(CX18_VERSION);
/* Generic utility functions */
int cx18_msleep_timeout(unsigned int msecs, int intr)
{
- int timeout = msecs_to_jiffies(msecs);
+ long int timeout = msecs_to_jiffies(msecs);
int sig;
do {
@@ -366,20 +428,69 @@ static void cx18_process_options(struct cx18 *cx)
{
int i, j;
- cx->options.megabytes[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers;
cx->options.megabytes[CX18_ENC_STREAM_TYPE_TS] = enc_ts_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_buffers;
cx->options.megabytes[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers;
cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers;
cx->options.megabytes[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control only */
+
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control, no data */
+
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = 0; /* computed later */
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control no data */
+
+ /* Except for VBI ensure stream_buffers & stream_buf_size are valid */
+ for (i = 0; i < CX18_MAX_STREAMS; i++) {
+ /* User said to use 0 buffers */
+ if (cx->stream_buffers[i] == 0) {
+ cx->options.megabytes[i] = 0;
+ cx->stream_buf_size[i] = 0;
+ continue;
+ }
+ /* User said to use 0 MB total */
+ if (cx->options.megabytes[i] <= 0) {
+ cx->options.megabytes[i] = 0;
+ cx->stream_buffers[i] = 0;
+ cx->stream_buf_size[i] = 0;
+ continue;
+ }
+ /* VBI is computed later or user said buffer has size 0 */
+ if (cx->stream_buf_size[i] <= 0) {
+ if (i != CX18_ENC_STREAM_TYPE_VBI) {
+ cx->options.megabytes[i] = 0;
+ cx->stream_buffers[i] = 0;
+ cx->stream_buf_size[i] = 0;
+ }
+ continue;
+ }
+ if (cx->stream_buffers[i] < 0) {
+ cx->stream_buffers[i] = cx->options.megabytes[i] * 1024
+ / cx->stream_buf_size[i];
+ } else {
+ /* N.B. This might round down to 0 */
+ cx->options.megabytes[i] =
+ cx->stream_buffers[i] * cx->stream_buf_size[i] / 1024;
+ }
+ cx->stream_buf_size[i] *= 1024; /* convert from kB to bytes */
+ }
+
cx->options.cardtype = cardtype[cx->num];
cx->options.tuner = tuner[cx->num];
cx->options.radio = radio[cx->num];
- if (mmio_ndelay[cx->num] < 0)
- cx->options.mmio_ndelay = CX18_DEFAULT_MMIO_NDELAY;
- else
- cx->options.mmio_ndelay = mmio_ndelay[cx->num];
-
cx->std = cx18_parse_std(cx);
if (cx->options.cardtype == -1) {
CX18_INFO("Ignore card\n");
@@ -440,22 +551,30 @@ done:
*/
static int __devinit cx18_init_struct1(struct cx18 *cx)
{
+ int i;
+
cx->base_addr = pci_resource_start(cx->dev, 0);
mutex_init(&cx->serialize_lock);
mutex_init(&cx->i2c_bus_lock[0]);
mutex_init(&cx->i2c_bus_lock[1]);
mutex_init(&cx->gpio_lock);
+ mutex_init(&cx->epu2apu_mb_lock);
+ mutex_init(&cx->epu2cpu_mb_lock);
spin_lock_init(&cx->lock);
cx->work_queue = create_singlethread_workqueue(cx->name);
if (cx->work_queue == NULL) {
- CX18_ERR("Could not create work queue\n");
- return -1;
+ CX18_ERR("Unable to create work hander thread\n");
+ return -ENOMEM;
}
- INIT_WORK(&cx->work, cx18_work_handler);
+ for (i = 0; i < CX18_MAX_EPU_WORK_ORDERS; i++) {
+ cx->epu_work_order[i].cx = cx;
+ cx->epu_work_order[i].str = cx->epu_debug_str;
+ INIT_WORK(&cx->epu_work_order[i].work, cx18_epu_work_handler);
+ }
/* start counting open_id at 1 */
cx->open_id = 1;
@@ -472,20 +591,55 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)
init_waitqueue_head(&cx->cap_w);
init_waitqueue_head(&cx->mb_apu_waitq);
init_waitqueue_head(&cx->mb_cpu_waitq);
- init_waitqueue_head(&cx->mb_epu_waitq);
- init_waitqueue_head(&cx->mb_hpu_waitq);
init_waitqueue_head(&cx->dma_waitq);
/* VBI */
- cx->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
+ cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced;
- cx->vbi.raw_size = 1456;
- cx->vbi.raw_decoder_line_size = 1456;
- cx->vbi.raw_decoder_sav_odd_field = 0x20;
- cx->vbi.raw_decoder_sav_even_field = 0x60;
- cx->vbi.sliced_decoder_line_size = 272;
- cx->vbi.sliced_decoder_sav_odd_field = 0xB0;
- cx->vbi.sliced_decoder_sav_even_field = 0xF0;
+
+ /*
+ * The VBI line sizes depend on the pixel clock and the horiz rate
+ *
+ * (1/Fh)*(2*Fp) = Samples/line
+ * = 4 bytes EAV + Anc data in hblank + 4 bytes SAV + active samples
+ *
+ * Sliced VBI is sent as ancillary data during horizontal blanking
+ * Raw VBI is sent as active video samples during vertcal blanking
+ *
+ * We use a BT.656 pxiel clock of 13.5 MHz and a BT.656 active line
+ * length of 720 pixels @ 4:2:2 sampling. Thus...
+ *
+ * For systems that use a 15.734 kHz horizontal rate, such as
+ * NTSC-M, PAL-M, PAL-60, and other 60 Hz/525 line systems, we have:
+ *
+ * (1/15.734 kHz) * 2 * 13.5 MHz = 1716 samples/line =
+ * 4 bytes SAV + 268 bytes anc data + 4 bytes SAV + 1440 active samples
+ *
+ * For systems that use a 15.625 kHz horizontal rate, such as
+ * PAL-B/G/H, PAL-I, SECAM-L and other 50 Hz/625 line systems, we have:
+ *
+ * (1/15.625 kHz) * 2 * 13.5 MHz = 1728 samples/line =
+ * 4 bytes SAV + 280 bytes anc data + 4 bytes SAV + 1440 active samples
+ *
+ */
+
+ /* FIXME: init these based on tuner std & modify when std changes */
+ /* CX18-AV-Core number of VBI samples output per horizontal line */
+ cx->vbi.raw_decoder_line_size = 1444; /* 4 byte SAV + 2 * 720 */
+ cx->vbi.sliced_decoder_line_size = 272; /* 60 Hz: 268+4, 50 Hz: 280+4 */
+
+ /* CX18-AV-Core VBI samples/line possibly rounded up */
+ cx->vbi.raw_size = 1444; /* Real max size is 1444 */
+ cx->vbi.sliced_size = 284; /* Real max size is 284 */
+
+ /*
+ * CX18-AV-Core SAV/EAV RP codes in VIP 1.x mode
+ * Task Field VerticalBlank HorizontalBlank 0 0 0 0
+ */
+ cx->vbi.raw_decoder_sav_odd_field = 0x20; /* V */
+ cx->vbi.raw_decoder_sav_even_field = 0x60; /* FV */
+ cx->vbi.sliced_decoder_sav_odd_field = 0xB0; /* T VH - actually EAV */
+ cx->vbi.sliced_decoder_sav_even_field = 0xF0; /* TFVH - actually EAV */
return 0;
}
@@ -518,6 +672,7 @@ static void __devinit cx18_init_struct2(struct cx18 *cx)
cx->av_state.aud_input = CX18_AV_AUDIO8;
cx->av_state.audclk_freq = 48000;
cx->av_state.audmode = V4L2_TUNER_MODE_LANG1;
+ /* FIXME - 8 is NTSC value, investigate */
cx->av_state.vbi_line_offset = 8;
}
@@ -662,12 +817,9 @@ static int __devinit cx18_probe(struct pci_dev *dev,
/* PCI Device Setup */
retval = cx18_setup_pci(cx, dev, pci_id);
- if (retval != 0) {
- if (retval == -EIO)
- goto free_workqueue;
- else if (retval == -ENXIO)
- goto free_mem;
- }
+ if (retval != 0)
+ goto free_workqueue;
+
/* save cx in the pci struct for later use */
pci_set_drvdata(dev, cx);
@@ -726,6 +878,7 @@ static int __devinit cx18_probe(struct pci_dev *dev,
goto free_i2c;
}
cx18_init_memory(cx);
+ cx18_init_scb(cx);
/* Register IRQ */
retval = request_irq(cx->dev->irq, cx18_irq_handler,
@@ -739,8 +892,6 @@ static int __devinit cx18_probe(struct pci_dev *dev,
cx->std = V4L2_STD_NTSC_M;
if (cx->options.tuner == -1) {
- int i;
-
for (i = 0; i < CX18_CARD_MAX_TUNERS; i++) {
if ((cx->std & cx->card->tuners[i].std) == 0)
continue;
@@ -777,13 +928,23 @@ static int __devinit cx18_probe(struct pci_dev *dev,
}
cx->params.video_gop_size = cx->is_60hz ? 15 : 12;
- cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = 0x08000;
- cx->stream_buf_size[CX18_ENC_STREAM_TYPE_TS] = 0x08000;
- cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = 0x01200;
- cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = 0x20000;
+ /*
+ * FIXME: setting the buffer size based on the tuner standard is
+ * suboptimal, as the CVBS and SVideo inputs could use a different std
+ * and the buffer could end up being too small in that case.
+ */
vbi_buf_size = cx->vbi.raw_size * (cx->is_60hz ? 24 : 36) / 2;
cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_buf_size;
+ if (cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] < 0)
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] =
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] * 1024 * 1024
+ / vbi_buf_size;
+ else
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] =
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] * vbi_buf_size
+ / (1024 * 1024);
+
if (cx->options.radio > 0)
cx->v4l2_cap |= V4L2_CAP_RADIO;
@@ -844,7 +1005,6 @@ err:
if (retval == 0)
retval = -ENODEV;
CX18_ERR("Error %d on initialization\n", retval);
- cx18_log_statistics(cx);
i = cx->num;
spin_lock(&cx18_cards_lock);
@@ -923,6 +1083,13 @@ int cx18_init_on_first_open(struct cx18 *cx)
return 0;
}
+static void cx18_cancel_epu_work_orders(struct cx18 *cx)
+{
+ int i;
+ for (i = 0; i < CX18_MAX_EPU_WORK_ORDERS; i++)
+ cancel_work_sync(&cx->epu_work_order[i].work);
+}
+
static void cx18_remove(struct pci_dev *pci_dev)
{
struct cx18 *cx = pci_get_drvdata(pci_dev);
@@ -940,7 +1107,8 @@ static void cx18_remove(struct pci_dev *pci_dev)
cx18_halt_firmware(cx);
- flush_workqueue(cx->work_queue);
+ cx18_cancel_epu_work_orders(cx);
+
destroy_workqueue(cx->work_queue);
cx18_streams_cleanup(cx, 1);
@@ -955,7 +1123,6 @@ static void cx18_remove(struct pci_dev *pci_dev)
pci_disable_device(cx->dev);
- cx18_log_statistics(cx);
CX18_INFO("Removed %s, card #%d\n", cx->card_name, cx->num);
}
@@ -1004,6 +1171,7 @@ static void module_cleanup(void)
continue;
kfree(cx18_cards[i]);
}
+
}
module_init(module_start);
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
index bbdd5f25041..0d2edebc39b 100644
--- a/drivers/media/video/cx18/cx18-driver.h
+++ b/drivers/media/video/cx18/cx18-driver.h
@@ -4,6 +4,7 @@
* Derived from ivtv-driver.h
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* 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
@@ -64,9 +65,6 @@
# error "This driver requires kernel PCI support."
#endif
-/* Default delay to throttle mmio access to the CX23418 */
-#define CX18_DEFAULT_MMIO_NDELAY 0 /* 0 ns = 0 PCI clock(s) / 33 MHz */
-
#define CX18_MEM_OFFSET 0x00000000
#define CX18_MEM_SIZE 0x04000000
#define CX18_REG_OFFSET 0x02000000
@@ -117,6 +115,17 @@
#define CX18_DEFAULT_ENC_VBI_BUFFERS 1
#define CX18_DEFAULT_ENC_PCM_BUFFERS 1
+/* Maximum firmware DMA buffers per stream */
+#define CX18_MAX_FW_MDLS_PER_STREAM 63
+
+/* DMA buffer, default size in kB allocated */
+#define CX18_DEFAULT_ENC_TS_BUFSIZE 32
+#define CX18_DEFAULT_ENC_MPG_BUFSIZE 32
+#define CX18_DEFAULT_ENC_IDX_BUFSIZE 32
+#define CX18_DEFAULT_ENC_YUV_BUFSIZE 128
+/* Default VBI bufsize based on standards supported by card tuner for now */
+#define CX18_DEFAULT_ENC_PCM_BUFSIZE 4
+
/* i2c stuff */
#define I2C_CLIENTS_MAX 16
@@ -176,7 +185,6 @@
#define CX18_MAX_PGM_INDEX (400)
-extern int cx18_retry_mmio; /* enable check & retry of mmio accesses */
extern int cx18_debug;
@@ -185,7 +193,6 @@ struct cx18_options {
int cardtype; /* force card type on load */
int tuner; /* set tuner on load */
int radio; /* enable/disable radio */
- unsigned long mmio_ndelay; /* delay in ns after every PCI mmio access */
};
/* per-buffer bit flags */
@@ -203,11 +210,8 @@ struct cx18_options {
#define CX18_F_I_EOS 4 /* End of encoder stream */
#define CX18_F_I_RADIO_USER 5 /* radio tuner is selected */
#define CX18_F_I_ENC_PAUSED 13 /* the encoder is paused */
-#define CX18_F_I_HAVE_WORK 15 /* there is work to be done */
-#define CX18_F_I_WORK_HANDLER_DVB 18 /* work to be done for DVB */
#define CX18_F_I_INITED 21 /* set after first open */
#define CX18_F_I_FAILED 22 /* set if first open failed */
-#define CX18_F_I_WORK_INITED 23 /* worker thread initialized */
/* These are the VBI types as they appear in the embedded VBI private packets. */
#define CX18_SLICED_TYPE_TELETEXT_B (1)
@@ -220,6 +224,7 @@ struct cx18_buffer {
dma_addr_t dma_handle;
u32 id;
unsigned long b_flags;
+ unsigned skipped;
char *buf;
u32 bytesused;
@@ -248,6 +253,27 @@ struct cx18_dvb {
struct cx18; /* forward reference */
struct cx18_scb; /* forward reference */
+
+#define CX18_MAX_MDL_ACKS 2
+#define CX18_MAX_EPU_WORK_ORDERS (CX18_MAX_FW_MDLS_PER_STREAM + 7)
+/* CPU_DE_RELEASE_MDL can burst CX18_MAX_FW_MDLS_PER_STREAM orders in a group */
+
+#define CX18_F_EWO_MB_STALE_UPON_RECEIPT 0x1
+#define CX18_F_EWO_MB_STALE_WHILE_PROC 0x2
+#define CX18_F_EWO_MB_STALE \
+ (CX18_F_EWO_MB_STALE_UPON_RECEIPT | CX18_F_EWO_MB_STALE_WHILE_PROC)
+
+struct cx18_epu_work_order {
+ struct work_struct work;
+ atomic_t pending;
+ struct cx18 *cx;
+ unsigned long flags;
+ int rpu;
+ struct cx18_mailbox mb;
+ struct cx18_mdl_ack mdl_ack[CX18_MAX_MDL_ACKS];
+ char *str;
+};
+
#define CX18_INVALID_TASK_HANDLE 0xffffffff
struct cx18_stream {
@@ -261,7 +287,7 @@ struct cx18_stream {
unsigned mdl_offset;
u32 id;
- spinlock_t qlock; /* locks access to the queues */
+ struct mutex qlock; /* locks access to the queues */
unsigned long s_flags; /* status flags, see above */
int dma; /* can be PCI_DMA_TODEVICE,
PCI_DMA_FROMDEVICE or
@@ -275,8 +301,8 @@ struct cx18_stream {
/* Buffer Queues */
struct cx18_queue q_free; /* free buffers */
- struct cx18_queue q_full; /* full buffers */
- struct cx18_queue q_io; /* waiting for I/O */
+ struct cx18_queue q_busy; /* busy buffers - in use by firmware */
+ struct cx18_queue q_full; /* full buffers - data for user apps */
/* DVB / Digital Transport */
struct cx18_dvb dvb;
@@ -353,12 +379,7 @@ struct cx18_i2c_algo_callback_data {
int bus_index; /* 0 or 1 for the cx23418's 1st or 2nd I2C bus */
};
-#define CX18_MAX_MMIO_RETRIES 10
-
-struct cx18_mmio_stats {
- atomic_t retried_write[CX18_MAX_MMIO_RETRIES+1];
- atomic_t retried_read[CX18_MAX_MMIO_RETRIES+1];
-};
+#define CX18_MAX_MMIO_WR_RETRIES 10
/* Struct to hold info about cx18 cards */
struct cx18 {
@@ -378,7 +399,9 @@ struct cx18 {
u32 v4l2_cap; /* V4L2 capabilities of card */
u32 hw_flags; /* Hardware description of the board */
unsigned mdl_offset;
- struct cx18_scb __iomem *scb; /* pointer to SCB */
+ struct cx18_scb __iomem *scb; /* pointer to SCB */
+ struct mutex epu2apu_mb_lock; /* protect driver to chip mailbox in SCB*/
+ struct mutex epu2cpu_mb_lock; /* protect driver to chip mailbox in SCB*/
struct cx18_av_state av_state;
@@ -397,6 +420,7 @@ struct cx18 {
struct mutex serialize_lock; /* mutex used to serialize open/close/start/stop/ioctl operations */
struct cx18_options options; /* User options */
+ int stream_buffers[CX18_MAX_STREAMS]; /* # of buffers for each stream */
int stream_buf_size[CX18_MAX_STREAMS]; /* Stream buffer size */
struct cx18_stream streams[CX18_MAX_STREAMS]; /* Stream data */
unsigned long i_flags; /* global cx18 flags */
@@ -428,14 +452,17 @@ struct cx18 {
wait_queue_head_t mb_apu_waitq;
wait_queue_head_t mb_cpu_waitq;
- wait_queue_head_t mb_epu_waitq;
- wait_queue_head_t mb_hpu_waitq;
wait_queue_head_t cap_w;
/* when the current DMA is finished this queue is woken up */
wait_queue_head_t dma_waitq;
+ u32 sw1_irq_mask;
+ u32 sw2_irq_mask;
+ u32 hw2_irq_mask;
+
struct workqueue_struct *work_queue;
- struct work_struct work;
+ struct cx18_epu_work_order epu_work_order[CX18_MAX_EPU_WORK_ORDERS];
+ char epu_debug_str[256]; /* CX18_EPU_DEBUG is rare: use shared space */
/* i2c */
struct i2c_adapter i2c_adap[2];
@@ -450,9 +477,6 @@ struct cx18 {
u32 gpio_val;
struct mutex gpio_lock;
- /* Statistics */
- struct cx18_mmio_stats mmio_stats;
-
/* v4l2 and User settings */
/* codec settings */
@@ -481,4 +505,10 @@ void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv);
/* First-open initialization: load firmware, etc. */
int cx18_init_on_first_open(struct cx18 *cx);
+/* Test if the current VBI mode is raw (1) or sliced (0) */
+static inline int cx18_raw_vbi(const struct cx18 *cx)
+{
+ return cx->vbi.in.type == V4L2_BUF_TYPE_VBI_CAPTURE;
+}
+
#endif /* CX18_DRIVER_H */
diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c
index 4542e2e5e3d..bd5e6f3fd4d 100644
--- a/drivers/media/video/cx18/cx18-dvb.c
+++ b/drivers/media/video/cx18/cx18-dvb.c
@@ -2,6 +2,7 @@
* cx18 functions for DVB support
*
* Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* 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
@@ -23,8 +24,6 @@
#include "cx18-dvb.h"
#include "cx18-io.h"
#include "cx18-streams.h"
-#include "cx18-queue.h"
-#include "cx18-scb.h"
#include "cx18-cards.h"
#include "s5h1409.h"
#include "mxl5005s.h"
@@ -109,20 +108,23 @@ static int cx18_dvb_start_feed(struct dvb_demux_feed *feed)
if (!demux->dmx.frontend)
return -EINVAL;
- if (stream) {
- mutex_lock(&stream->dvb.feedlock);
- if (stream->dvb.feeding++ == 0) {
- CX18_DEBUG_INFO("Starting Transport DMA\n");
- ret = cx18_start_v4l2_encode_stream(stream);
- if (ret < 0) {
- CX18_DEBUG_INFO(
- "Failed to start Transport DMA\n");
- stream->dvb.feeding--;
- }
- } else
- ret = 0;
- mutex_unlock(&stream->dvb.feedlock);
- }
+ if (!stream)
+ return -EINVAL;
+
+ mutex_lock(&stream->dvb.feedlock);
+ if (stream->dvb.feeding++ == 0) {
+ CX18_DEBUG_INFO("Starting Transport DMA\n");
+ set_bit(CX18_F_S_STREAMING, &stream->s_flags);
+ ret = cx18_start_v4l2_encode_stream(stream);
+ if (ret < 0) {
+ CX18_DEBUG_INFO("Failed to start Transport DMA\n");
+ stream->dvb.feeding--;
+ if (stream->dvb.feeding == 0)
+ clear_bit(CX18_F_S_STREAMING, &stream->s_flags);
+ }
+ } else
+ ret = 0;
+ mutex_unlock(&stream->dvb.feedlock);
return ret;
}
@@ -215,6 +217,10 @@ int cx18_dvb_register(struct cx18_stream *stream)
dvb_net_init(dvb_adapter, &dvb->dvbnet, dmx);
CX18_INFO("DVB Frontend registered\n");
+ CX18_INFO("Registered DVB adapter%d for %s (%d x %d kB)\n",
+ stream->dvb.dvb_adapter.num, stream->name,
+ stream->buffers, stream->buf_size/1024);
+
mutex_init(&dvb->feedlock);
dvb->enabled = 1;
return ret;
@@ -302,24 +308,3 @@ static int dvb_register(struct cx18_stream *stream)
return ret;
}
-
-void cx18_dvb_work_handler(struct cx18 *cx)
-{
- struct cx18_buffer *buf;
- struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_TS];
-
- while ((buf = cx18_dequeue(s, &s->q_full)) != NULL) {
- if (s->dvb.enabled)
- dvb_dmx_swfilter(&s->dvb.demux, buf->buf,
- buf->bytesused);
-
- cx18_enqueue(s, buf, &s->q_free);
- cx18_buf_sync_for_device(s, buf);
- if (s->handle == CX18_INVALID_TASK_HANDLE) /* FIXME: improve */
- continue;
-
- cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
- (void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
- 1, buf->id, s->buf_size);
- }
-}
diff --git a/drivers/media/video/cx18/cx18-dvb.h b/drivers/media/video/cx18/cx18-dvb.h
index bbdcefc87f2..bf8d8f6f545 100644
--- a/drivers/media/video/cx18/cx18-dvb.h
+++ b/drivers/media/video/cx18/cx18-dvb.h
@@ -23,4 +23,3 @@
int cx18_dvb_register(struct cx18_stream *stream);
void cx18_dvb_unregister(struct cx18_stream *stream);
-void cx18_dvb_work_handler(struct cx18 *cx);
diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c
index 5f908990754..425271a2951 100644
--- a/drivers/media/video/cx18/cx18-fileops.c
+++ b/drivers/media/video/cx18/cx18-fileops.c
@@ -4,6 +4,7 @@
* Derived from ivtv-fileops.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* 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
@@ -66,12 +67,11 @@ static int cx18_claim_stream(struct cx18_open_id *id, int type)
}
s->id = id->open_id;
- /* CX18_DEC_STREAM_TYPE_MPG needs to claim CX18_DEC_STREAM_TYPE_VBI,
- CX18_ENC_STREAM_TYPE_MPG needs to claim CX18_ENC_STREAM_TYPE_VBI
+ /* CX18_ENC_STREAM_TYPE_MPG needs to claim CX18_ENC_STREAM_TYPE_VBI
(provided VBI insertion is on and sliced VBI is selected), for all
other streams we're done */
if (type == CX18_ENC_STREAM_TYPE_MPG &&
- cx->vbi.insert_mpeg && cx->vbi.sliced_in->service_set) {
+ cx->vbi.insert_mpeg && !cx18_raw_vbi(cx)) {
vbi_type = CX18_ENC_STREAM_TYPE_VBI;
} else {
return 0;
@@ -185,8 +185,10 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block,
!test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) {
while ((buf = cx18_dequeue(s_vbi, &s_vbi->q_full))) {
/* byteswap and process VBI data */
-/* cx18_process_vbi_data(cx, buf, s_vbi->dma_pts, s_vbi->type); */
- cx18_enqueue(s_vbi, buf, &s_vbi->q_free);
+ cx18_process_vbi_data(cx, buf,
+ s_vbi->dma_pts,
+ s_vbi->type);
+ cx18_stream_put_buf_fw(s_vbi, buf);
}
}
buf = &cx->vbi.sliced_mpeg_buf;
@@ -194,11 +196,6 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block,
return buf;
}
- /* do we have leftover data? */
- buf = cx18_dequeue(s, &s->q_io);
- if (buf)
- return buf;
-
/* do we have new data? */
buf = cx18_dequeue(s, &s->q_full);
if (buf) {
@@ -262,7 +259,7 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s,
if (len > ucount)
len = ucount;
if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG &&
- cx->vbi.sliced_in->service_set && buf != &cx->vbi.sliced_mpeg_buf) {
+ !cx18_raw_vbi(cx) && buf != &cx->vbi.sliced_mpeg_buf) {
const char *start = buf->buf + buf->readpos;
const char *p = start + 1;
const u8 *q;
@@ -337,8 +334,7 @@ static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf,
/* Each VBI buffer is one frame, the v4l2 API says that for VBI the
frames should arrive one-by-one, so make sure we never output more
than one VBI frame at a time */
- if (s->type == CX18_ENC_STREAM_TYPE_VBI &&
- cx->vbi.sliced_in->service_set)
+ if (s->type == CX18_ENC_STREAM_TYPE_VBI && !cx18_raw_vbi(cx))
single_frame = 1;
for (;;) {
@@ -365,16 +361,10 @@ static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf,
tot_count - tot_written);
if (buf != &cx->vbi.sliced_mpeg_buf) {
- if (buf->readpos == buf->bytesused) {
- cx18_buf_sync_for_device(s, buf);
- cx18_enqueue(s, buf, &s->q_free);
- cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5,
- s->handle,
- (void __iomem *)&cx->scb->cpu_mdl[buf->id] -
- cx->enc_mem,
- 1, buf->id, s->buf_size);
- } else
- cx18_enqueue(s, buf, &s->q_io);
+ if (buf->readpos == buf->bytesused)
+ cx18_stream_put_buf_fw(s, buf);
+ else
+ cx18_push(s, buf, &s->q_full);
} else if (buf->readpos == buf->bytesused) {
int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES;
@@ -518,7 +508,7 @@ unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait)
CX18_DEBUG_HI_FILE("Encoder poll\n");
poll_wait(filp, &s->waitq, wait);
- if (atomic_read(&s->q_full.buffers) || atomic_read(&s->q_io.buffers))
+ if (atomic_read(&s->q_full.buffers))
return POLLIN | POLLRDNORM;
if (eof)
return POLLHUP;
diff --git a/drivers/media/video/cx18/cx18-firmware.c b/drivers/media/video/cx18/cx18-firmware.c
index 51534428cd0..1fa95da1575 100644
--- a/drivers/media/video/cx18/cx18-firmware.c
+++ b/drivers/media/video/cx18/cx18-firmware.c
@@ -2,6 +2,7 @@
* cx18 firmware functions
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* 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
@@ -25,6 +26,7 @@
#include "cx18-irq.h"
#include "cx18-firmware.h"
#include "cx18-cards.h"
+#include "cx18-av-core.h"
#include <linux/firmware.h>
#define CX18_PROC_SOFT_RESET 0xc70010
@@ -121,6 +123,7 @@ static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx)
if (cx18_raw_readl(cx, dst) != *src) {
CX18_ERR("Mismatch at offset %x\n", i);
release_firmware(fw);
+ cx18_setup_page(cx, 0);
return -EIO;
}
dst++;
@@ -131,10 +134,12 @@ static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx)
CX18_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size);
size = fw->size;
release_firmware(fw);
+ cx18_setup_page(cx, SCB_OFFSET);
return size;
}
-static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx)
+static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx,
+ u32 *entry_addr)
{
const struct firmware *fw = NULL;
int i, j;
@@ -149,9 +154,11 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx)
if (request_firmware(&fw, fn, &cx->dev->dev)) {
CX18_ERR("unable to open firmware %s\n", fn);
CX18_ERR("did you put the firmware in the hotplug firmware directory?\n");
+ cx18_setup_page(cx, 0);
return -ENOMEM;
}
+ *entry_addr = 0;
src = (const u32 *)fw->data;
vers = fw->data + sizeof(seghdr);
sz = fw->size;
@@ -168,10 +175,12 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx)
}
CX18_DEBUG_INFO("load segment %x-%x\n", seghdr.addr,
seghdr.addr + seghdr.size - 1);
+ if (*entry_addr == 0)
+ *entry_addr = seghdr.addr;
if (offset + seghdr.size > sz)
break;
for (i = 0; i < seghdr.size; i += 4096) {
- cx18_setup_page(cx, offset + i);
+ cx18_setup_page(cx, seghdr.addr + i);
for (j = i; j < seghdr.size && j < i + 4096; j += 4) {
/* no need for endianness conversion on the ppc */
cx18_raw_writel(cx, src[(offset + j) / 4],
@@ -181,6 +190,7 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx)
CX18_ERR("Mismatch at offset %x\n",
offset + j);
release_firmware(fw);
+ cx18_setup_page(cx, 0);
return -EIO;
}
}
@@ -192,16 +202,17 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx)
fn, apu_version, fw->size);
size = fw->size;
release_firmware(fw);
- /* Clear bit0 for APU to start from 0 */
- cx18_write_reg(cx, cx18_read_reg(cx, 0xc72030) & ~1, 0xc72030);
+ cx18_setup_page(cx, 0);
return size;
}
void cx18_halt_firmware(struct cx18 *cx)
{
CX18_DEBUG_INFO("Preparing for firmware halt.\n");
- cx18_write_reg(cx, 0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */
- cx18_write_reg(cx, 0x00020002, CX18_ADEC_CONTROL);
+ cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET,
+ 0x0000000F, 0x000F000F);
+ cx18_write_reg_expect(cx, 0x00020002, CX18_ADEC_CONTROL,
+ 0x00000002, 0x00020002);
}
void cx18_init_power(struct cx18 *cx, int lowpwr)
@@ -211,9 +222,48 @@ void cx18_init_power(struct cx18 *cx, int lowpwr)
cx18_write_reg(cx, 0x00000008, CX18_PLL_POWER_DOWN);
/* ADEC out of sleep */
- cx18_write_reg(cx, 0x00020000, CX18_ADEC_CONTROL);
-
- /* The fast clock is at 200/245 MHz */
+ cx18_write_reg_expect(cx, 0x00020000, CX18_ADEC_CONTROL,
+ 0x00000000, 0x00020002);
+
+ /*
+ * The PLL parameters are based on the external crystal frequency that
+ * would ideally be:
+ *
+ * NTSC Color subcarrier freq * 8 =
+ * 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz
+ *
+ * The accidents of history and rationale that explain from where this
+ * combination of magic numbers originate can be found in:
+ *
+ * [1] Abrahams, I. C., "Choice of Chrominance Subcarrier Frequency in
+ * the NTSC Standards", Proceedings of the I-R-E, January 1954, pp 79-80
+ *
+ * [2] Abrahams, I. C., "The 'Frequency Interleaving' Principle in the
+ * NTSC Standards", Proceedings of the I-R-E, January 1954, pp 81-83
+ *
+ * As Mike Bradley has rightly pointed out, it's not the exact crystal
+ * frequency that matters, only that all parts of the driver and
+ * firmware are using the same value (close to the ideal value).
+ *
+ * Since I have a strong suspicion that, if the firmware ever assumes a
+ * crystal value at all, it will assume 28.636360 MHz, the crystal
+ * freq used in calculations in this driver will be:
+ *
+ * xtal_freq = 28.636360 MHz
+ *
+ * an error of less than 0.13 ppm which is way, way better than any off
+ * the shelf crystal will have for accuracy anyway.
+ *
+ * Below I aim to run the PLLs' VCOs near 400 MHz to minimze errors.
+ *
+ * Many thanks to Jeff Campbell and Mike Bradley for their extensive
+ * investigation, experimentation, testing, and suggested solutions of
+ * of audio/video sync problems with SVideo and CVBS captures.
+ */
+
+ /* the fast clock is at 200/245 MHz */
+ /* 1 * xtal_freq * 0x0d.f7df9b8 / 2 = 200 MHz: 400 MHz pre post-divide*/
+ /* 1 * xtal_freq * 0x11.1c71eb8 / 2 = 245 MHz: 490 MHz pre post-divide*/
cx18_write_reg(cx, lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT);
cx18_write_reg(cx, lowpwr ? 0x1EFBF37 : 0x038E3D7,
CX18_FAST_CLOCK_PLL_FRAC);
@@ -223,16 +273,36 @@ void cx18_init_power(struct cx18 *cx, int lowpwr)
cx18_write_reg(cx, 4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH);
/* set slow clock to 125/120 MHz */
- cx18_write_reg(cx, lowpwr ? 0x11 : 0x10, CX18_SLOW_CLOCK_PLL_INT);
- cx18_write_reg(cx, lowpwr ? 0xEBAF05 : 0x18618A8,
+ /* xtal_freq * 0x0d.1861a20 / 3 = 125 MHz: 375 MHz before post-divide */
+ /* xtal_freq * 0x0c.92493f8 / 3 = 120 MHz: 360 MHz before post-divide */
+ cx18_write_reg(cx, lowpwr ? 0xD : 0xC, CX18_SLOW_CLOCK_PLL_INT);
+ cx18_write_reg(cx, lowpwr ? 0x30C344 : 0x124927F,
CX18_SLOW_CLOCK_PLL_FRAC);
- cx18_write_reg(cx, 4, CX18_SLOW_CLOCK_PLL_POST);
+ cx18_write_reg(cx, 3, CX18_SLOW_CLOCK_PLL_POST);
/* mpeg clock pll 54MHz */
+ /* xtal_freq * 0xf.15f17f0 / 8 = 54 MHz: 432 MHz before post-divide */
cx18_write_reg(cx, 0xF, CX18_MPEG_CLOCK_PLL_INT);
- cx18_write_reg(cx, 0x2BCFEF, CX18_MPEG_CLOCK_PLL_FRAC);
+ cx18_write_reg(cx, 0x2BE2FE, CX18_MPEG_CLOCK_PLL_FRAC);
cx18_write_reg(cx, 8, CX18_MPEG_CLOCK_PLL_POST);
+ /*
+ * VDCLK Integer = 0x0f, Post Divider = 0x04
+ * AIMCLK Integer = 0x0e, Post Divider = 0x16
+ */
+ cx18_av_write4(cx, CXADEC_PLL_CTRL1, 0x160e040f);
+
+ /* VDCLK Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz before post divide */
+ cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, 0x002be2fe);
+
+ /* AIMCLK Fraction = 0x05227ad */
+ /* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz before post-divide */
+ cx18_av_write4(cx, CXADEC_AUX_PLL_FRAC, 0x005227ad);
+
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */
+ cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56);
+
/* Defaults */
/* APU = SC or SC/2 = 125/62.5 */
/* EPU = SC = 125 */
@@ -248,22 +318,34 @@ void cx18_init_power(struct cx18 *cx, int lowpwr)
/* VFC = disabled */
/* USB = disabled */
- cx18_write_reg(cx, lowpwr ? 0xFFFF0020 : 0x00060004,
- CX18_CLOCK_SELECT1);
- cx18_write_reg(cx, lowpwr ? 0xFFFF0004 : 0x00060006,
- CX18_CLOCK_SELECT2);
-
- cx18_write_reg(cx, 0xFFFF0002, CX18_HALF_CLOCK_SELECT1);
- cx18_write_reg(cx, 0xFFFF0104, CX18_HALF_CLOCK_SELECT2);
+ if (lowpwr) {
+ cx18_write_reg_expect(cx, 0xFFFF0020, CX18_CLOCK_SELECT1,
+ 0x00000020, 0xFFFFFFFF);
+ cx18_write_reg_expect(cx, 0xFFFF0004, CX18_CLOCK_SELECT2,
+ 0x00000004, 0xFFFFFFFF);
+ } else {
+ /* This doesn't explicitly set every clock select */
+ cx18_write_reg_expect(cx, 0x00060004, CX18_CLOCK_SELECT1,
+ 0x00000004, 0x00060006);
+ cx18_write_reg_expect(cx, 0x00060006, CX18_CLOCK_SELECT2,
+ 0x00000006, 0x00060006);
+ }
- cx18_write_reg(cx, 0xFFFF9026, CX18_CLOCK_ENABLE1);
- cx18_write_reg(cx, 0xFFFF3105, CX18_CLOCK_ENABLE2);
+ cx18_write_reg_expect(cx, 0xFFFF0002, CX18_HALF_CLOCK_SELECT1,
+ 0x00000002, 0xFFFFFFFF);
+ cx18_write_reg_expect(cx, 0xFFFF0104, CX18_HALF_CLOCK_SELECT2,
+ 0x00000104, 0xFFFFFFFF);
+ cx18_write_reg_expect(cx, 0xFFFF9026, CX18_CLOCK_ENABLE1,
+ 0x00009026, 0xFFFFFFFF);
+ cx18_write_reg_expect(cx, 0xFFFF3105, CX18_CLOCK_ENABLE2,
+ 0x00003105, 0xFFFFFFFF);
}
void cx18_init_memory(struct cx18 *cx)
{
cx18_msleep_timeout(10, 0);
- cx18_write_reg(cx, 0x10000, CX18_DDR_SOFT_RESET);
+ cx18_write_reg_expect(cx, 0x00010000, CX18_DDR_SOFT_RESET,
+ 0x00000000, 0x00010001);
cx18_msleep_timeout(10, 0);
cx18_write_reg(cx, cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG);
@@ -282,13 +364,15 @@ void cx18_init_memory(struct cx18 *cx)
cx18_msleep_timeout(10, 0);
- cx18_write_reg(cx, 0x20000, CX18_DDR_SOFT_RESET);
+ cx18_write_reg_expect(cx, 0x00020000, CX18_DDR_SOFT_RESET,
+ 0x00000000, 0x00020002);
cx18_msleep_timeout(10, 0);
/* use power-down mode when idle */
cx18_write_reg(cx, 0x00000010, CX18_DDR_POWER_REG);
- cx18_write_reg(cx, 0x10001, CX18_REG_BUS_TIMEOUT_EN);
+ cx18_write_reg_expect(cx, 0x00010001, CX18_REG_BUS_TIMEOUT_EN,
+ 0x00000001, 0x00010001);
cx18_write_reg(cx, 0x48, CX18_DDR_MB_PER_ROW_7);
cx18_write_reg(cx, 0xE0000, CX18_DDR_BASE_63_ADDR);
@@ -307,51 +391,76 @@ void cx18_init_memory(struct cx18 *cx)
int cx18_firmware_init(struct cx18 *cx)
{
+ u32 fw_entry_addr;
+ int sz, retries;
+ u32 api_args[MAX_MB_ARGUMENTS];
+
/* Allow chip to control CLKRUN */
cx18_write_reg(cx, 0x5, CX18_DSP0_INTERRUPT_MASK);
- cx18_write_reg(cx, 0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */
+ /* Stop the firmware */
+ cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET,
+ 0x0000000F, 0x000F000F);
cx18_msleep_timeout(1, 0);
+ /* If the CPU is still running */
+ if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) == 0) {
+ CX18_ERR("%s: couldn't stop CPU to load firmware\n", __func__);
+ return -EIO;
+ }
+
cx18_sw1_irq_enable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
cx18_sw2_irq_enable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
- /* Only if the processor is not running */
- if (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) {
- int sz = load_apu_fw_direct("v4l-cx23418-apu.fw",
- cx->enc_mem, cx);
-
- cx18_write_enc(cx, 0xE51FF004, 0);
- cx18_write_enc(cx, 0xa00000, 4); /* todo: not hardcoded */
- /* Start APU */
- cx18_write_reg(cx, 0x00010000, CX18_PROC_SOFT_RESET);
- cx18_msleep_timeout(500, 0);
-
- sz = sz <= 0 ? sz : load_cpu_fw_direct("v4l-cx23418-cpu.fw",
- cx->enc_mem, cx);
-
- if (sz > 0) {
- int retries = 0;
-
- /* start the CPU */
- cx18_write_reg(cx, 0x00080000, CX18_PROC_SOFT_RESET);
- while (retries++ < 50) { /* Loop for max 500mS */
- if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET)
- & 1) == 0)
- break;
- cx18_msleep_timeout(10, 0);
- }
- cx18_msleep_timeout(200, 0);
- if (retries == 51) {
- CX18_ERR("Could not start the CPU\n");
- return -EIO;
- }
- }
- if (sz <= 0)
- return -EIO;
+ sz = load_cpu_fw_direct("v4l-cx23418-cpu.fw", cx->enc_mem, cx);
+ if (sz <= 0)
+ return sz;
+
+ /* The SCB & IPC area *must* be correct before starting the firmwares */
+ cx18_init_scb(cx);
+
+ fw_entry_addr = 0;
+ sz = load_apu_fw_direct("v4l-cx23418-apu.fw", cx->enc_mem, cx,
+ &fw_entry_addr);
+ if (sz <= 0)
+ return sz;
+
+ /* Start the CPU. The CPU will take care of the APU for us. */
+ cx18_write_reg_expect(cx, 0x00080000, CX18_PROC_SOFT_RESET,
+ 0x00000000, 0x00080008);
+
+ /* Wait up to 500 ms for the APU to come out of reset */
+ for (retries = 0;
+ retries < 50 && (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1;
+ retries++)
+ cx18_msleep_timeout(10, 0);
+
+ cx18_msleep_timeout(200, 0);
+
+ if (retries == 50 &&
+ (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1) {
+ CX18_ERR("Could not start the CPU\n");
+ return -EIO;
}
+
+ /*
+ * The CPU had once before set up to receive an interrupt for it's
+ * outgoing IRQ_CPU_TO_EPU_ACK to us. If it ever does this, we get an
+ * interrupt when it sends us an ack, but by the time we process it,
+ * that flag in the SW2 status register has been cleared by the CPU
+ * firmware. We'll prevent that not so useful condition from happening
+ * by clearing the CPU's interrupt enables for Ack IRQ's we want to
+ * process.
+ */
+ cx18_sw2_irq_disable_cpu(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
+
+ /* Try a benign command to see if the CPU is alive and well */
+ sz = cx18_vapi_result(cx, api_args, CX18_CPU_DEBUG_PEEK32, 1, 0);
+ if (sz < 0)
+ return sz;
+
/* initialize GPIO */
- cx18_write_reg(cx, 0x14001400, 0xC78110);
+ cx18_write_reg_expect(cx, 0x14001400, 0xc78110, 0x00001400, 0x14001400);
return 0;
}
diff --git a/drivers/media/video/cx18/cx18-gpio.c b/drivers/media/video/cx18/cx18-gpio.c
index 0e560421989..1a99329f33c 100644
--- a/drivers/media/video/cx18/cx18-gpio.c
+++ b/drivers/media/video/cx18/cx18-gpio.c
@@ -4,6 +4,7 @@
* Derived from ivtv-gpio.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* 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
@@ -47,15 +48,19 @@
static void gpio_write(struct cx18 *cx)
{
- u32 dir = cx->gpio_dir;
- u32 val = cx->gpio_val;
-
- cx18_write_reg(cx, (dir & 0xffff) << 16, CX18_REG_GPIO_DIR1);
- cx18_write_reg(cx, ((dir & 0xffff) << 16) | (val & 0xffff),
- CX18_REG_GPIO_OUT1);
- cx18_write_reg(cx, dir & 0xffff0000, CX18_REG_GPIO_DIR2);
- cx18_write_reg_sync(cx, (dir & 0xffff0000) | ((val & 0xffff0000) >> 16),
- CX18_REG_GPIO_OUT2);
+ u32 dir_lo = cx->gpio_dir & 0xffff;
+ u32 val_lo = cx->gpio_val & 0xffff;
+ u32 dir_hi = cx->gpio_dir >> 16;
+ u32 val_hi = cx->gpio_val >> 16;
+
+ cx18_write_reg_expect(cx, dir_lo << 16,
+ CX18_REG_GPIO_DIR1, ~dir_lo, dir_lo);
+ cx18_write_reg_expect(cx, (dir_lo << 16) | val_lo,
+ CX18_REG_GPIO_OUT1, val_lo, dir_lo);
+ cx18_write_reg_expect(cx, dir_hi << 16,
+ CX18_REG_GPIO_DIR2, ~dir_hi, dir_hi);
+ cx18_write_reg_expect(cx, (dir_hi << 16) | val_hi,
+ CX18_REG_GPIO_OUT2, val_hi, dir_hi);
}
void cx18_reset_i2c_slaves_gpio(struct cx18 *cx)
diff --git a/drivers/media/video/cx18/cx18-gpio.h b/drivers/media/video/cx18/cx18-gpio.h
index beb7424b994..39ffccc19d8 100644
--- a/drivers/media/video/cx18/cx18-gpio.h
+++ b/drivers/media/video/cx18/cx18-gpio.h
@@ -4,6 +4,7 @@
* Derived from ivtv-gpio.h
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* 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
diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c
index aa09e557b19..8941f58bed7 100644
--- a/drivers/media/video/cx18/cx18-i2c.c
+++ b/drivers/media/video/cx18/cx18-i2c.c
@@ -4,6 +4,7 @@
* Derived from ivtv-i2c.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* 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
@@ -27,6 +28,7 @@
#include "cx18-gpio.h"
#include "cx18-av-core.h"
#include "cx18-i2c.h"
+#include "cx18-irq.h"
#define CX18_REG_I2C_1_WR 0xf15000
#define CX18_REG_I2C_1_RD 0xf15008
@@ -160,9 +162,9 @@ static void cx18_setscl(void *data, int state)
u32 r = cx18_read_reg(cx, addr);
if (state)
- cx18_write_reg_sync(cx, r | SETSCL_BIT, addr);
+ cx18_write_reg(cx, r | SETSCL_BIT, addr);
else
- cx18_write_reg_sync(cx, r & ~SETSCL_BIT, addr);
+ cx18_write_reg(cx, r & ~SETSCL_BIT, addr);
}
static void cx18_setsda(void *data, int state)
@@ -173,9 +175,9 @@ static void cx18_setsda(void *data, int state)
u32 r = cx18_read_reg(cx, addr);
if (state)
- cx18_write_reg_sync(cx, r | SETSDL_BIT, addr);
+ cx18_write_reg(cx, r | SETSDL_BIT, addr);
else
- cx18_write_reg_sync(cx, r & ~SETSDL_BIT, addr);
+ cx18_write_reg(cx, r & ~SETSDL_BIT, addr);
}
static int cx18_getscl(void *data)
@@ -396,30 +398,33 @@ int init_cx18_i2c(struct cx18 *cx)
if (cx18_read_reg(cx, CX18_REG_I2C_2_WR) != 0x0003c02f) {
/* Reset/Unreset I2C hardware block */
/* Clock select 220MHz */
- cx18_write_reg(cx, 0x10000000, 0xc71004);
+ cx18_write_reg_expect(cx, 0x10000000, 0xc71004,
+ 0x00000000, 0x10001000);
/* Clock Enable */
- cx18_write_reg_sync(cx, 0x10001000, 0xc71024);
+ cx18_write_reg_expect(cx, 0x10001000, 0xc71024,
+ 0x00001000, 0x10001000);
}
/* courtesy of Steven Toth <stoth@hauppauge.com> */
- cx18_write_reg_sync(cx, 0x00c00000, 0xc7001c);
+ cx18_write_reg_expect(cx, 0x00c00000, 0xc7001c, 0x00000000, 0x00c000c0);
mdelay(10);
- cx18_write_reg_sync(cx, 0x00c000c0, 0xc7001c);
+ cx18_write_reg_expect(cx, 0x00c000c0, 0xc7001c, 0x000000c0, 0x00c000c0);
mdelay(10);
- cx18_write_reg_sync(cx, 0x00c00000, 0xc7001c);
+ cx18_write_reg_expect(cx, 0x00c00000, 0xc7001c, 0x00000000, 0x00c000c0);
mdelay(10);
/* Set to edge-triggered intrs. */
- cx18_write_reg_sync(cx, 0x00c00000, 0xc730c8);
+ cx18_write_reg(cx, 0x00c00000, 0xc730c8);
/* Clear any stale intrs */
- cx18_write_reg_sync(cx, 0x00c00000, 0xc730c4);
+ cx18_write_reg_expect(cx, HW2_I2C1_INT|HW2_I2C2_INT, HW2_INT_CLR_STATUS,
+ ~(HW2_I2C1_INT|HW2_I2C2_INT), HW2_I2C1_INT|HW2_I2C2_INT);
/* Hw I2C1 Clock Freq ~100kHz */
- cx18_write_reg_sync(cx, 0x00021c0f & ~4, CX18_REG_I2C_1_WR);
+ cx18_write_reg(cx, 0x00021c0f & ~4, CX18_REG_I2C_1_WR);
cx18_setscl(&cx->i2c_algo_cb_data[0], 1);
cx18_setsda(&cx->i2c_algo_cb_data[0], 1);
/* Hw I2C2 Clock Freq ~100kHz */
- cx18_write_reg_sync(cx, 0x00021c0f & ~4, CX18_REG_I2C_2_WR);
+ cx18_write_reg(cx, 0x00021c0f & ~4, CX18_REG_I2C_2_WR);
cx18_setscl(&cx->i2c_algo_cb_data[1], 1);
cx18_setsda(&cx->i2c_algo_cb_data[1], 1);
diff --git a/drivers/media/video/cx18/cx18-io.c b/drivers/media/video/cx18/cx18-io.c
index 220fae8d4ad..ec5b3d7bcc6 100644
--- a/drivers/media/video/cx18/cx18-io.c
+++ b/drivers/media/video/cx18/cx18-io.c
@@ -24,179 +24,6 @@
#include "cx18-io.h"
#include "cx18-irq.h"
-void cx18_log_statistics(struct cx18 *cx)
-{
- int i;
-
- if (!(cx18_debug & CX18_DBGFLG_INFO))
- return;
-
- for (i = 0; i <= CX18_MAX_MMIO_RETRIES; i++)
- CX18_DEBUG_INFO("retried_write[%d] = %d\n", i,
- atomic_read(&cx->mmio_stats.retried_write[i]));
- for (i = 0; i <= CX18_MAX_MMIO_RETRIES; i++)
- CX18_DEBUG_INFO("retried_read[%d] = %d\n", i,
- atomic_read(&cx->mmio_stats.retried_read[i]));
- return;
-}
-
-void cx18_raw_writel_retry(struct cx18 *cx, u32 val, void __iomem *addr)
-{
- int i;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- cx18_raw_writel_noretry(cx, val, addr);
- if (val == cx18_raw_readl_noretry(cx, addr))
- break;
- }
- cx18_log_write_retries(cx, i, addr);
-}
-
-u32 cx18_raw_readl_retry(struct cx18 *cx, const void __iomem *addr)
-{
- int i;
- u32 val;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- val = cx18_raw_readl_noretry(cx, addr);
- if (val != 0xffffffff) /* PCI bus read error */
- break;
- }
- cx18_log_read_retries(cx, i, addr);
- return val;
-}
-
-u16 cx18_raw_readw_retry(struct cx18 *cx, const void __iomem *addr)
-{
- int i;
- u16 val;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- val = cx18_raw_readw_noretry(cx, addr);
- if (val != 0xffff) /* PCI bus read error */
- break;
- }
- cx18_log_read_retries(cx, i, addr);
- return val;
-}
-
-void cx18_writel_retry(struct cx18 *cx, u32 val, void __iomem *addr)
-{
- int i;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- cx18_writel_noretry(cx, val, addr);
- if (val == cx18_readl_noretry(cx, addr))
- break;
- }
- cx18_log_write_retries(cx, i, addr);
-}
-
-void _cx18_writel_expect(struct cx18 *cx, u32 val, void __iomem *addr,
- u32 eval, u32 mask)
-{
- int i;
- eval &= mask;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- cx18_writel_noretry(cx, val, addr);
- if (eval == (cx18_readl_noretry(cx, addr) & mask))
- break;
- }
- cx18_log_write_retries(cx, i, addr);
-}
-
-void cx18_writew_retry(struct cx18 *cx, u16 val, void __iomem *addr)
-{
- int i;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- cx18_writew_noretry(cx, val, addr);
- if (val == cx18_readw_noretry(cx, addr))
- break;
- }
- cx18_log_write_retries(cx, i, addr);
-}
-
-void cx18_writeb_retry(struct cx18 *cx, u8 val, void __iomem *addr)
-{
- int i;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- cx18_writeb_noretry(cx, val, addr);
- if (val == cx18_readb_noretry(cx, addr))
- break;
- }
- cx18_log_write_retries(cx, i, addr);
-}
-
-u32 cx18_readl_retry(struct cx18 *cx, const void __iomem *addr)
-{
- int i;
- u32 val;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- val = cx18_readl_noretry(cx, addr);
- if (val != 0xffffffff) /* PCI bus read error */
- break;
- }
- cx18_log_read_retries(cx, i, addr);
- return val;
-}
-
-u16 cx18_readw_retry(struct cx18 *cx, const void __iomem *addr)
-{
- int i;
- u16 val;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- val = cx18_readw_noretry(cx, addr);
- if (val != 0xffff) /* PCI bus read error */
- break;
- }
- cx18_log_read_retries(cx, i, addr);
- return val;
-}
-
-u8 cx18_readb_retry(struct cx18 *cx, const void __iomem *addr)
-{
- int i;
- u8 val;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- val = cx18_readb_noretry(cx, addr);
- if (val != 0xff) /* PCI bus read error */
- break;
- }
- cx18_log_read_retries(cx, i, addr);
- return val;
-}
-
-void cx18_memcpy_fromio(struct cx18 *cx, void *to,
- const void __iomem *from, unsigned int len)
-{
- const u8 __iomem *src = from;
- u8 *dst = to;
-
- /* Align reads on the CX23418's addresses */
- if ((len > 0) && ((unsigned long) src & 1)) {
- *dst = cx18_readb(cx, src);
- len--;
- dst++;
- src++;
- }
- if ((len > 1) && ((unsigned long) src & 2)) {
- *((u16 *)dst) = cx18_raw_readw(cx, src);
- len -= 2;
- dst += 2;
- src += 2;
- }
- while (len > 3) {
- *((u32 *)dst) = cx18_raw_readl(cx, src);
- len -= 4;
- dst += 4;
- src += 4;
- }
- if (len > 1) {
- *((u16 *)dst) = cx18_raw_readw(cx, src);
- len -= 2;
- dst += 2;
- src += 2;
- }
- if (len > 0)
- *dst = cx18_readb(cx, src);
-}
-
void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count)
{
u8 __iomem *dst = addr;
@@ -230,32 +57,35 @@ void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count)
void cx18_sw1_irq_enable(struct cx18 *cx, u32 val)
{
- u32 r;
cx18_write_reg_expect(cx, val, SW1_INT_STATUS, ~val, val);
- r = cx18_read_reg(cx, SW1_INT_ENABLE_PCI);
- cx18_write_reg(cx, r | val, SW1_INT_ENABLE_PCI);
+ cx->sw1_irq_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI) | val;
+ cx18_write_reg(cx, cx->sw1_irq_mask, SW1_INT_ENABLE_PCI);
}
void cx18_sw1_irq_disable(struct cx18 *cx, u32 val)
{
- u32 r;
- r = cx18_read_reg(cx, SW1_INT_ENABLE_PCI);
- cx18_write_reg(cx, r & ~val, SW1_INT_ENABLE_PCI);
+ cx->sw1_irq_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI) & ~val;
+ cx18_write_reg(cx, cx->sw1_irq_mask, SW1_INT_ENABLE_PCI);
}
void cx18_sw2_irq_enable(struct cx18 *cx, u32 val)
{
- u32 r;
cx18_write_reg_expect(cx, val, SW2_INT_STATUS, ~val, val);
- r = cx18_read_reg(cx, SW2_INT_ENABLE_PCI);
- cx18_write_reg(cx, r | val, SW2_INT_ENABLE_PCI);
+ cx->sw2_irq_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI) | val;
+ cx18_write_reg(cx, cx->sw2_irq_mask, SW2_INT_ENABLE_PCI);
}
void cx18_sw2_irq_disable(struct cx18 *cx, u32 val)
{
+ cx->sw2_irq_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI) & ~val;
+ cx18_write_reg(cx, cx->sw2_irq_mask, SW2_INT_ENABLE_PCI);
+}
+
+void cx18_sw2_irq_disable_cpu(struct cx18 *cx, u32 val)
+{
u32 r;
- r = cx18_read_reg(cx, SW2_INT_ENABLE_PCI);
- cx18_write_reg(cx, r & ~val, SW2_INT_ENABLE_PCI);
+ r = cx18_read_reg(cx, SW2_INT_ENABLE_CPU);
+ cx18_write_reg(cx, r & ~val, SW2_INT_ENABLE_CPU);
}
void cx18_setup_page(struct cx18 *cx, u32 addr)
diff --git a/drivers/media/video/cx18/cx18-io.h b/drivers/media/video/cx18/cx18-io.h
index 425244453ea..2635b3a8cc9 100644
--- a/drivers/media/video/cx18/cx18-io.h
+++ b/drivers/media/video/cx18/cx18-io.h
@@ -25,232 +25,125 @@
#include "cx18-driver.h"
-static inline void cx18_io_delay(struct cx18 *cx)
-{
- if (cx->options.mmio_ndelay)
- ndelay(cx->options.mmio_ndelay);
-}
-
/*
* Readback and retry of MMIO access for reliability:
* The concept was suggested by Steve Toth <stoth@linuxtv.org>.
* The implmentation is the fault of Andy Walls <awalls@radix.net>.
+ *
+ * *write* functions are implied to retry the mmio unless suffixed with _noretry
+ * *read* functions never retry the mmio (it never helps to do so)
*/
-/* Statistics gathering */
-static inline
-void cx18_log_write_retries(struct cx18 *cx, int i, const void __iomem *addr)
-{
- if (i > CX18_MAX_MMIO_RETRIES)
- i = CX18_MAX_MMIO_RETRIES;
- atomic_inc(&cx->mmio_stats.retried_write[i]);
- return;
-}
-
-static inline
-void cx18_log_read_retries(struct cx18 *cx, int i, const void __iomem *addr)
+/* Non byteswapping memory mapped IO */
+static inline u32 cx18_raw_readl(struct cx18 *cx, const void __iomem *addr)
{
- if (i > CX18_MAX_MMIO_RETRIES)
- i = CX18_MAX_MMIO_RETRIES;
- atomic_inc(&cx->mmio_stats.retried_read[i]);
- return;
+ return __raw_readl(addr);
}
-void cx18_log_statistics(struct cx18 *cx);
-
-/* Non byteswapping memory mapped IO */
static inline
void cx18_raw_writel_noretry(struct cx18 *cx, u32 val, void __iomem *addr)
{
__raw_writel(val, addr);
- cx18_io_delay(cx);
}
-void cx18_raw_writel_retry(struct cx18 *cx, u32 val, void __iomem *addr);
-
static inline void cx18_raw_writel(struct cx18 *cx, u32 val, void __iomem *addr)
{
- if (cx18_retry_mmio)
- cx18_raw_writel_retry(cx, val, addr);
- else
+ int i;
+ for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) {
cx18_raw_writel_noretry(cx, val, addr);
+ if (val == cx18_raw_readl(cx, addr))
+ break;
+ }
}
-
-static inline
-u32 cx18_raw_readl_noretry(struct cx18 *cx, const void __iomem *addr)
-{
- u32 ret = __raw_readl(addr);
- cx18_io_delay(cx);
- return ret;
-}
-
-u32 cx18_raw_readl_retry(struct cx18 *cx, const void __iomem *addr);
-
-static inline u32 cx18_raw_readl(struct cx18 *cx, const void __iomem *addr)
+/* Normal memory mapped IO */
+static inline u32 cx18_readl(struct cx18 *cx, const void __iomem *addr)
{
- if (cx18_retry_mmio)
- return cx18_raw_readl_retry(cx, addr);
-
- return cx18_raw_readl_noretry(cx, addr);
+ return readl(addr);
}
-
static inline
-u16 cx18_raw_readw_noretry(struct cx18 *cx, const void __iomem *addr)
+void cx18_writel_noretry(struct cx18 *cx, u32 val, void __iomem *addr)
{
- u16 ret = __raw_readw(addr);
- cx18_io_delay(cx);
- return ret;
+ writel(val, addr);
}
-u16 cx18_raw_readw_retry(struct cx18 *cx, const void __iomem *addr);
-
-static inline u16 cx18_raw_readw(struct cx18 *cx, const void __iomem *addr)
+static inline void cx18_writel(struct cx18 *cx, u32 val, void __iomem *addr)
{
- if (cx18_retry_mmio)
- return cx18_raw_readw_retry(cx, addr);
-
- return cx18_raw_readw_noretry(cx, addr);
+ int i;
+ for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) {
+ cx18_writel_noretry(cx, val, addr);
+ if (val == cx18_readl(cx, addr))
+ break;
+ }
}
-
-/* Normal memory mapped IO */
static inline
-void cx18_writel_noretry(struct cx18 *cx, u32 val, void __iomem *addr)
+void cx18_writel_expect(struct cx18 *cx, u32 val, void __iomem *addr,
+ u32 eval, u32 mask)
{
- writel(val, addr);
- cx18_io_delay(cx);
+ int i;
+ u32 r;
+ eval &= mask;
+ for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) {
+ cx18_writel_noretry(cx, val, addr);
+ r = cx18_readl(cx, addr);
+ if (r == 0xffffffff && eval != 0xffffffff)
+ continue;
+ if (eval == (r & mask))
+ break;
+ }
}
-void cx18_writel_retry(struct cx18 *cx, u32 val, void __iomem *addr);
-
-static inline void cx18_writel(struct cx18 *cx, u32 val, void __iomem *addr)
+static inline u16 cx18_readw(struct cx18 *cx, const void __iomem *addr)
{
- if (cx18_retry_mmio)
- cx18_writel_retry(cx, val, addr);
- else
- cx18_writel_noretry(cx, val, addr);
+ return readw(addr);
}
-void _cx18_writel_expect(struct cx18 *cx, u32 val, void __iomem *addr,
- u32 eval, u32 mask);
-
static inline
void cx18_writew_noretry(struct cx18 *cx, u16 val, void __iomem *addr)
{
writew(val, addr);
- cx18_io_delay(cx);
}
-void cx18_writew_retry(struct cx18 *cx, u16 val, void __iomem *addr);
-
static inline void cx18_writew(struct cx18 *cx, u16 val, void __iomem *addr)
{
- if (cx18_retry_mmio)
- cx18_writew_retry(cx, val, addr);
- else
+ int i;
+ for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) {
cx18_writew_noretry(cx, val, addr);
+ if (val == cx18_readw(cx, addr))
+ break;
+ }
}
+static inline u8 cx18_readb(struct cx18 *cx, const void __iomem *addr)
+{
+ return readb(addr);
+}
static inline
void cx18_writeb_noretry(struct cx18 *cx, u8 val, void __iomem *addr)
{
writeb(val, addr);
- cx18_io_delay(cx);
}
-void cx18_writeb_retry(struct cx18 *cx, u8 val, void __iomem *addr);
-
static inline void cx18_writeb(struct cx18 *cx, u8 val, void __iomem *addr)
{
- if (cx18_retry_mmio)
- cx18_writeb_retry(cx, val, addr);
- else
+ int i;
+ for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) {
cx18_writeb_noretry(cx, val, addr);
-}
-
-
-static inline u32 cx18_readl_noretry(struct cx18 *cx, const void __iomem *addr)
-{
- u32 ret = readl(addr);
- cx18_io_delay(cx);
- return ret;
-}
-
-u32 cx18_readl_retry(struct cx18 *cx, const void __iomem *addr);
-
-static inline u32 cx18_readl(struct cx18 *cx, const void __iomem *addr)
-{
- if (cx18_retry_mmio)
- return cx18_readl_retry(cx, addr);
-
- return cx18_readl_noretry(cx, addr);
-}
-
-
-static inline u16 cx18_readw_noretry(struct cx18 *cx, const void __iomem *addr)
-{
- u16 ret = readw(addr);
- cx18_io_delay(cx);
- return ret;
-}
-
-u16 cx18_readw_retry(struct cx18 *cx, const void __iomem *addr);
-
-static inline u16 cx18_readw(struct cx18 *cx, const void __iomem *addr)
-{
- if (cx18_retry_mmio)
- return cx18_readw_retry(cx, addr);
-
- return cx18_readw_noretry(cx, addr);
-}
-
-
-static inline u8 cx18_readb_noretry(struct cx18 *cx, const void __iomem *addr)
-{
- u8 ret = readb(addr);
- cx18_io_delay(cx);
- return ret;
-}
-
-u8 cx18_readb_retry(struct cx18 *cx, const void __iomem *addr);
-
-static inline u8 cx18_readb(struct cx18 *cx, const void __iomem *addr)
-{
- if (cx18_retry_mmio)
- return cx18_readb_retry(cx, addr);
-
- return cx18_readb_noretry(cx, addr);
-}
-
-
-static inline
-u32 cx18_write_sync_noretry(struct cx18 *cx, u32 val, void __iomem *addr)
-{
- cx18_writel_noretry(cx, val, addr);
- return cx18_readl_noretry(cx, addr);
+ if (val == cx18_readb(cx, addr))
+ break;
+ }
}
static inline
-u32 cx18_write_sync_retry(struct cx18 *cx, u32 val, void __iomem *addr)
-{
- cx18_writel_retry(cx, val, addr);
- return cx18_readl_retry(cx, addr);
-}
-
-static inline u32 cx18_write_sync(struct cx18 *cx, u32 val, void __iomem *addr)
+void cx18_memcpy_fromio(struct cx18 *cx, void *to,
+ const void __iomem *from, unsigned int len)
{
- if (cx18_retry_mmio)
- return cx18_write_sync_retry(cx, val, addr);
-
- return cx18_write_sync_noretry(cx, val, addr);
+ memcpy_fromio(to, from, len);
}
-
-void cx18_memcpy_fromio(struct cx18 *cx, void *to,
- const void __iomem *from, unsigned int len);
void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count);
@@ -260,136 +153,39 @@ static inline void cx18_write_reg_noretry(struct cx18 *cx, u32 val, u32 reg)
cx18_writel_noretry(cx, val, cx->reg_mem + reg);
}
-static inline void cx18_write_reg_retry(struct cx18 *cx, u32 val, u32 reg)
-{
- cx18_writel_retry(cx, val, cx->reg_mem + reg);
-}
-
static inline void cx18_write_reg(struct cx18 *cx, u32 val, u32 reg)
{
- if (cx18_retry_mmio)
- cx18_write_reg_retry(cx, val, reg);
- else
- cx18_write_reg_noretry(cx, val, reg);
-}
-
-static inline void _cx18_write_reg_expect(struct cx18 *cx, u32 val, u32 reg,
- u32 eval, u32 mask)
-{
- _cx18_writel_expect(cx, val, cx->reg_mem + reg, eval, mask);
+ cx18_writel(cx, val, cx->reg_mem + reg);
}
static inline void cx18_write_reg_expect(struct cx18 *cx, u32 val, u32 reg,
u32 eval, u32 mask)
{
- if (cx18_retry_mmio)
- _cx18_write_reg_expect(cx, val, reg, eval, mask);
- else
- cx18_write_reg_noretry(cx, val, reg);
-}
-
-
-static inline u32 cx18_read_reg_noretry(struct cx18 *cx, u32 reg)
-{
- return cx18_readl_noretry(cx, cx->reg_mem + reg);
-}
-
-static inline u32 cx18_read_reg_retry(struct cx18 *cx, u32 reg)
-{
- return cx18_readl_retry(cx, cx->reg_mem + reg);
+ cx18_writel_expect(cx, val, cx->reg_mem + reg, eval, mask);
}
static inline u32 cx18_read_reg(struct cx18 *cx, u32 reg)
{
- if (cx18_retry_mmio)
- return cx18_read_reg_retry(cx, reg);
-
- return cx18_read_reg_noretry(cx, reg);
-}
-
-
-static inline u32 cx18_write_reg_sync_noretry(struct cx18 *cx, u32 val, u32 reg)
-{
- return cx18_write_sync_noretry(cx, val, cx->reg_mem + reg);
-}
-
-static inline u32 cx18_write_reg_sync_retry(struct cx18 *cx, u32 val, u32 reg)
-{
- return cx18_write_sync_retry(cx, val, cx->reg_mem + reg);
-}
-
-static inline u32 cx18_write_reg_sync(struct cx18 *cx, u32 val, u32 reg)
-{
- if (cx18_retry_mmio)
- return cx18_write_reg_sync_retry(cx, val, reg);
-
- return cx18_write_reg_sync_noretry(cx, val, reg);
+ return cx18_readl(cx, cx->reg_mem + reg);
}
/* Access "encoder memory" region of CX23418 memory mapped I/O */
-static inline void cx18_write_enc_noretry(struct cx18 *cx, u32 val, u32 addr)
-{
- cx18_writel_noretry(cx, val, cx->enc_mem + addr);
-}
-
-static inline void cx18_write_enc_retry(struct cx18 *cx, u32 val, u32 addr)
-{
- cx18_writel_retry(cx, val, cx->enc_mem + addr);
-}
-
static inline void cx18_write_enc(struct cx18 *cx, u32 val, u32 addr)
{
- if (cx18_retry_mmio)
- cx18_write_enc_retry(cx, val, addr);
- else
- cx18_write_enc_noretry(cx, val, addr);
-}
-
-
-static inline u32 cx18_read_enc_noretry(struct cx18 *cx, u32 addr)
-{
- return cx18_readl_noretry(cx, cx->enc_mem + addr);
-}
-
-static inline u32 cx18_read_enc_retry(struct cx18 *cx, u32 addr)
-{
- return cx18_readl_retry(cx, cx->enc_mem + addr);
+ cx18_writel(cx, val, cx->enc_mem + addr);
}
static inline u32 cx18_read_enc(struct cx18 *cx, u32 addr)
{
- if (cx18_retry_mmio)
- return cx18_read_enc_retry(cx, addr);
-
- return cx18_read_enc_noretry(cx, addr);
-}
-
-static inline
-u32 cx18_write_enc_sync_noretry(struct cx18 *cx, u32 val, u32 addr)
-{
- return cx18_write_sync_noretry(cx, val, cx->enc_mem + addr);
-}
-
-static inline
-u32 cx18_write_enc_sync_retry(struct cx18 *cx, u32 val, u32 addr)
-{
- return cx18_write_sync_retry(cx, val, cx->enc_mem + addr);
-}
-
-static inline
-u32 cx18_write_enc_sync(struct cx18 *cx, u32 val, u32 addr)
-{
- if (cx18_retry_mmio)
- return cx18_write_enc_sync_retry(cx, val, addr);
-
- return cx18_write_enc_sync_noretry(cx, val, addr);
+ return cx18_readl(cx, cx->enc_mem + addr);
}
void cx18_sw1_irq_enable(struct cx18 *cx, u32 val);
void cx18_sw1_irq_disable(struct cx18 *cx, u32 val);
void cx18_sw2_irq_enable(struct cx18 *cx, u32 val);
void cx18_sw2_irq_disable(struct cx18 *cx, u32 val);
+void cx18_sw2_irq_disable_cpu(struct cx18 *cx, u32 val);
void cx18_setup_page(struct cx18 *cx, u32 addr);
#endif /* CX18_IO_H */
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
index f0ca50f5fdd..e6087486f88 100644
--- a/drivers/media/video/cx18/cx18-ioctl.c
+++ b/drivers/media/video/cx18/cx18-ioctl.c
@@ -4,6 +4,7 @@
* Derived from ivtv-ioctl.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* 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
@@ -237,13 +238,12 @@ static int cx18_s_fmt_vbi_cap(struct file *file, void *fh,
if (ret)
return ret;
- if (id->type == CX18_ENC_STREAM_TYPE_VBI &&
- cx->vbi.sliced_in->service_set &&
- atomic_read(&cx->ana_capturing) > 0)
+ if (!cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)
return -EBUSY;
cx->vbi.sliced_in->service_set = 0;
- cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in);
+ cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
return cx18_g_fmt_vbi_cap(file, fh, fmt);
}
@@ -745,14 +745,12 @@ static int cx18_log_status(struct file *file, void *fh)
continue;
CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n",
s->name, s->s_flags,
- (s->buffers - atomic_read(&s->q_free.buffers))
- * 100 / s->buffers,
+ atomic_read(&s->q_full.buffers) * 100 / s->buffers,
(s->buffers * s->buf_size) / 1024, s->buffers);
}
CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n",
(long long)cx->mpg_data_received,
(long long)cx->vbi_data_inserted);
- cx18_log_statistics(cx);
CX18_INFO("================== END STATUS CARD #%d ==================\n", cx->num);
return 0;
}
diff --git a/drivers/media/video/cx18/cx18-ioctl.h b/drivers/media/video/cx18/cx18-ioctl.h
index 2222f679d86..08fe24e9510 100644
--- a/drivers/media/video/cx18/cx18-ioctl.h
+++ b/drivers/media/video/cx18/cx18-ioctl.h
@@ -4,6 +4,7 @@
* Derived from ivtv-ioctl.h
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* 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
diff --git a/drivers/media/video/cx18/cx18-irq.c b/drivers/media/video/cx18/cx18-irq.c
index 5fbfbd0f149..af2f504eda2 100644
--- a/drivers/media/video/cx18/cx18-irq.c
+++ b/drivers/media/video/cx18/cx18-irq.c
@@ -2,6 +2,7 @@
* cx18 interrupt handling
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* 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
@@ -21,132 +22,9 @@
#include "cx18-driver.h"
#include "cx18-io.h"
-#include "cx18-firmware.h"
-#include "cx18-fileops.h"
-#include "cx18-queue.h"
#include "cx18-irq.h"
-#include "cx18-ioctl.h"
#include "cx18-mailbox.h"
-#include "cx18-vbi.h"
#include "cx18-scb.h"
-#include "cx18-dvb.h"
-
-void cx18_work_handler(struct work_struct *work)
-{
- struct cx18 *cx = container_of(work, struct cx18, work);
- if (test_and_clear_bit(CX18_F_I_WORK_INITED, &cx->i_flags)) {
- struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 };
- /* This thread must use the FIFO scheduler as it
- * is realtime sensitive. */
- sched_setscheduler(current, SCHED_FIFO, &param);
- }
- if (test_and_clear_bit(CX18_F_I_WORK_HANDLER_DVB, &cx->i_flags))
- cx18_dvb_work_handler(cx);
-}
-
-static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb)
-{
- u32 handle = mb->args[0];
- struct cx18_stream *s = NULL;
- struct cx18_buffer *buf;
- u32 off;
- int i;
- int id;
-
- for (i = 0; i < CX18_MAX_STREAMS; i++) {
- s = &cx->streams[i];
- if ((handle == s->handle) && (s->dvb.enabled))
- break;
- if (s->v4l2dev && handle == s->handle)
- break;
- }
- if (i == CX18_MAX_STREAMS) {
- CX18_WARN("Got DMA done notification for unknown/inactive"
- " handle %d\n", handle);
- mb->error = CXERR_NOT_OPEN;
- mb->cmd = 0;
- cx18_mb_ack(cx, mb);
- return;
- }
-
- off = mb->args[1];
- if (mb->args[2] != 1)
- CX18_WARN("Ack struct = %d for %s\n",
- mb->args[2], s->name);
- id = cx18_read_enc(cx, off);
- buf = cx18_queue_get_buf_irq(s, id, cx18_read_enc(cx, off + 4));
- CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id);
- if (buf) {
- cx18_buf_sync_for_cpu(s, buf);
- if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) {
- CX18_DEBUG_HI_DMA("TS recv bytesused = %d\n",
- buf->bytesused);
-
- set_bit(CX18_F_I_WORK_HANDLER_DVB, &cx->i_flags);
- set_bit(CX18_F_I_HAVE_WORK, &cx->i_flags);
- } else
- set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags);
- } else {
- CX18_WARN("Could not find buf %d for stream %s\n",
- cx18_read_enc(cx, off), s->name);
- }
- mb->error = 0;
- mb->cmd = 0;
- cx18_mb_ack(cx, mb);
- wake_up(&cx->dma_waitq);
- if (s->id != -1)
- wake_up(&s->waitq);
-}
-
-static void epu_debug(struct cx18 *cx, struct cx18_mailbox *mb)
-{
- char str[256] = { 0 };
- char *p;
-
- if (mb->args[1]) {
- cx18_setup_page(cx, mb->args[1]);
- cx18_memcpy_fromio(cx, str, cx->enc_mem + mb->args[1], 252);
- str[252] = 0;
- }
- cx18_mb_ack(cx, mb);
- CX18_DEBUG_INFO("%x %s\n", mb->args[0], str);
- p = strchr(str, '.');
- if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags) && p && p > str)
- CX18_INFO("FW version: %s\n", p - 1);
-}
-
-static void epu_cmd(struct cx18 *cx, u32 sw1)
-{
- struct cx18_mailbox mb;
-
- if (sw1 & IRQ_CPU_TO_EPU) {
- cx18_memcpy_fromio(cx, &mb, &cx->scb->cpu2epu_mb, sizeof(mb));
- mb.error = 0;
-
- switch (mb.cmd) {
- case CX18_EPU_DMA_DONE:
- epu_dma_done(cx, &mb);
- break;
- case CX18_EPU_DEBUG:
- epu_debug(cx, &mb);
- break;
- default:
- CX18_WARN("Unknown CPU_TO_EPU mailbox command %#08x\n",
- mb.cmd);
- break;
- }
- }
-
- if (sw1 & IRQ_APU_TO_EPU) {
- cx18_memcpy_fromio(cx, &mb, &cx->scb->apu2epu_mb, sizeof(mb));
- CX18_WARN("Unknown APU_TO_EPU mailbox command %#08x\n", mb.cmd);
- }
-
- if (sw1 & IRQ_HPU_TO_EPU) {
- cx18_memcpy_fromio(cx, &mb, &cx->scb->hpu2epu_mb, sizeof(mb));
- CX18_WARN("Unknown HPU_TO_EPU mailbox command %#08x\n", mb.cmd);
- }
-}
static void xpu_ack(struct cx18 *cx, u32 sw2)
{
@@ -154,23 +32,24 @@ static void xpu_ack(struct cx18 *cx, u32 sw2)
wake_up(&cx->mb_cpu_waitq);
if (sw2 & IRQ_APU_TO_EPU_ACK)
wake_up(&cx->mb_apu_waitq);
- if (sw2 & IRQ_HPU_TO_EPU_ACK)
- wake_up(&cx->mb_hpu_waitq);
+}
+
+static void epu_cmd(struct cx18 *cx, u32 sw1)
+{
+ if (sw1 & IRQ_CPU_TO_EPU)
+ cx18_api_epu_cmd_irq(cx, CPU);
+ if (sw1 & IRQ_APU_TO_EPU)
+ cx18_api_epu_cmd_irq(cx, APU);
}
irqreturn_t cx18_irq_handler(int irq, void *dev_id)
{
struct cx18 *cx = (struct cx18 *)dev_id;
- u32 sw1, sw1_mask;
- u32 sw2, sw2_mask;
- u32 hw2, hw2_mask;
+ u32 sw1, sw2, hw2;
- sw1_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI);
- sw1 = cx18_read_reg(cx, SW1_INT_STATUS) & sw1_mask;
- sw2_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI);
- sw2 = cx18_read_reg(cx, SW2_INT_STATUS) & sw2_mask;
- hw2_mask = cx18_read_reg(cx, HW2_INT_MASK5_PCI);
- hw2 = cx18_read_reg(cx, HW2_INT_CLR_STATUS) & hw2_mask;
+ sw1 = cx18_read_reg(cx, SW1_INT_STATUS) & cx->sw1_irq_mask;
+ sw2 = cx18_read_reg(cx, SW2_INT_STATUS) & cx->sw2_irq_mask;
+ hw2 = cx18_read_reg(cx, HW2_INT_CLR_STATUS) & cx->hw2_irq_mask;
if (sw1)
cx18_write_reg_expect(cx, sw1, SW1_INT_STATUS, ~sw1, sw1);
@@ -180,7 +59,15 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id)
cx18_write_reg_expect(cx, hw2, HW2_INT_CLR_STATUS, ~hw2, hw2);
if (sw1 || sw2 || hw2)
- CX18_DEBUG_HI_IRQ("SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2);
+ CX18_DEBUG_HI_IRQ("received interrupts "
+ "SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2);
+
+ /*
+ * SW1 responses have to happen first. The sending XPU times out the
+ * incoming mailboxes on us rather rapidly.
+ */
+ if (sw1)
+ epu_cmd(cx, sw1);
/* To do: interrupt-based I2C handling
if (hw2 & (HW2_I2C1_INT|HW2_I2C2_INT)) {
@@ -190,11 +77,5 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id)
if (sw2)
xpu_ack(cx, sw2);
- if (sw1)
- epu_cmd(cx, sw1);
-
- if (test_and_clear_bit(CX18_F_I_HAVE_WORK, &cx->i_flags))
- queue_work(cx->work_queue, &cx->work);
-
return (sw1 || sw2 || hw2) ? IRQ_HANDLED : IRQ_NONE;
}
diff --git a/drivers/media/video/cx18/cx18-irq.h b/drivers/media/video/cx18/cx18-irq.h
index 6173ca3bc9e..91f0b5278ef 100644
--- a/drivers/media/video/cx18/cx18-irq.h
+++ b/drivers/media/video/cx18/cx18-irq.h
@@ -2,6 +2,7 @@
* cx18 interrupt handling
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* 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
@@ -28,8 +29,7 @@
#define SW1_INT_ENABLE_PCI 0xc7311c
#define SW2_INT_SET 0xc73140
#define SW2_INT_STATUS 0xc73144
+#define SW2_INT_ENABLE_CPU 0xc73158
#define SW2_INT_ENABLE_PCI 0xc7315c
irqreturn_t cx18_irq_handler(int irq, void *dev_id);
-
-void cx18_work_handler(struct work_struct *work);
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c
index acff7dfb60d..de5e723fdf4 100644
--- a/drivers/media/video/cx18/cx18-mailbox.c
+++ b/drivers/media/video/cx18/cx18-mailbox.c
@@ -2,6 +2,7 @@
* cx18 mailbox functions
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* 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
@@ -26,15 +27,14 @@
#include "cx18-scb.h"
#include "cx18-irq.h"
#include "cx18-mailbox.h"
+#include "cx18-queue.h"
+#include "cx18-streams.h"
+
+static const char *rpu_str[] = { "APU", "CPU", "EPU", "HPU" };
#define API_FAST (1 << 2) /* Short timeout */
#define API_SLOW (1 << 3) /* Additional 300ms timeout */
-#define APU 0
-#define CPU 1
-#define EPU 2
-#define HPU 3
-
struct cx18_api_info {
u32 cmd;
u8 flags; /* Flags, see above */
@@ -82,8 +82,9 @@ static const struct cx18_api_info api_info[] = {
API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS, 0),
API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0),
API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST),
- API_ENTRY(CPU, CX18_APU_RESETAI, API_FAST),
API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, API_SLOW),
+ API_ENTRY(APU, CX18_APU_RESETAI, 0),
+ API_ENTRY(CPU, CX18_CPU_DEBUG_PEEK32, 0),
API_ENTRY(0, 0, 0),
};
@@ -97,70 +98,175 @@ static const struct cx18_api_info *find_api_info(u32 cmd)
return NULL;
}
-static struct cx18_mailbox __iomem *cx18_mb_is_complete(struct cx18 *cx, int rpu,
- u32 *state, u32 *irq, u32 *req)
+static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name)
{
- struct cx18_mailbox __iomem *mb = NULL;
- int wait_count = 0;
- u32 ack;
-
- switch (rpu) {
- case APU:
- mb = &cx->scb->epu2apu_mb;
- *state = cx18_readl(cx, &cx->scb->apu_state);
- *irq = cx18_readl(cx, &cx->scb->epu2apu_irq);
- break;
+ char argstr[MAX_MB_ARGUMENTS*11+1];
+ char *p;
+ int i;
- case CPU:
- mb = &cx->scb->epu2cpu_mb;
- *state = cx18_readl(cx, &cx->scb->cpu_state);
- *irq = cx18_readl(cx, &cx->scb->epu2cpu_irq);
- break;
+ if (!(cx18_debug & CX18_DBGFLG_API))
+ return;
- case HPU:
- mb = &cx->scb->epu2hpu_mb;
- *state = cx18_readl(cx, &cx->scb->hpu_state);
- *irq = cx18_readl(cx, &cx->scb->epu2hpu_irq);
- break;
+ for (i = 0, p = argstr; i < MAX_MB_ARGUMENTS; i++, p += 11) {
+ /* kernel snprintf() appends '\0' always */
+ snprintf(p, 12, " %#010x", mb->args[i]);
}
+ CX18_DEBUG_API("%s: req %#010x ack %#010x cmd %#010x err %#010x args%s"
+ "\n", name, mb->request, mb->ack, mb->cmd, mb->error, argstr);
+}
- if (mb == NULL)
- return mb;
- do {
- *req = cx18_readl(cx, &mb->request);
- ack = cx18_readl(cx, &mb->ack);
- wait_count++;
- } while (*req != ack && wait_count < 600);
+/*
+ * Functions that run in a work_queue work handling context
+ */
- if (*req == ack) {
- (*req)++;
- if (*req == 0 || *req == 0xffffffff)
- *req = 1;
- return mb;
+static void epu_dma_done(struct cx18 *cx, struct cx18_epu_work_order *order)
+{
+ u32 handle, mdl_ack_count, id;
+ struct cx18_mailbox *mb;
+ struct cx18_mdl_ack *mdl_ack;
+ struct cx18_stream *s;
+ struct cx18_buffer *buf;
+ int i;
+
+ mb = &order->mb;
+ handle = mb->args[0];
+ s = cx18_handle_to_stream(cx, handle);
+
+ if (s == NULL) {
+ CX18_WARN("Got DMA done notification for unknown/inactive"
+ " handle %d, %s mailbox seq no %d\n", handle,
+ (order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) ?
+ "stale" : "good", mb->request);
+ return;
}
- return NULL;
+
+ mdl_ack_count = mb->args[2];
+ mdl_ack = order->mdl_ack;
+ for (i = 0; i < mdl_ack_count; i++, mdl_ack++) {
+ id = mdl_ack->id;
+ /*
+ * Simple integrity check for processing a stale (and possibly
+ * inconsistent mailbox): make sure the buffer id is in the
+ * valid range for the stream.
+ *
+ * We go through the trouble of dealing with stale mailboxes
+ * because most of the time, the mailbox data is still valid and
+ * unchanged (and in practice the firmware ping-pongs the
+ * two mdl_ack buffers so mdl_acks are not stale).
+ *
+ * There are occasions when we get a half changed mailbox,
+ * which this check catches for a handle & id mismatch. If the
+ * handle and id do correspond, the worst case is that we
+ * completely lost the old buffer, but pick up the new buffer
+ * early (but the new mdl_ack is guaranteed to be good in this
+ * case as the firmware wouldn't point us to a new mdl_ack until
+ * it's filled in).
+ *
+ * cx18_queue_get buf() will detect the lost buffers
+ * and send them back to q_free for fw rotation eventually.
+ */
+ if ((order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) &&
+ !(id >= s->mdl_offset &&
+ id < (s->mdl_offset + s->buffers))) {
+ CX18_WARN("Fell behind! Ignoring stale mailbox with "
+ " inconsistent data. Lost buffer for mailbox "
+ "seq no %d\n", mb->request);
+ break;
+ }
+ buf = cx18_queue_get_buf(s, id, mdl_ack->data_used);
+
+ CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id);
+ if (buf == NULL) {
+ CX18_WARN("Could not find buf %d for stream %s\n",
+ id, s->name);
+ /* Put as many buffers as possible back into fw use */
+ cx18_stream_load_fw_queue(s);
+ continue;
+ }
+
+ if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) {
+ CX18_DEBUG_HI_DMA("TS recv bytesused = %d\n",
+ buf->bytesused);
+ dvb_dmx_swfilter(&s->dvb.demux, buf->buf,
+ buf->bytesused);
+ }
+ /* Put as many buffers as possible back into fw use */
+ cx18_stream_load_fw_queue(s);
+ /* Put back TS buffer, since it was removed from all queues */
+ if (s->type == CX18_ENC_STREAM_TYPE_TS)
+ cx18_stream_put_buf_fw(s, buf);
+ }
+ wake_up(&cx->dma_waitq);
+ if (s->id != -1)
+ wake_up(&s->waitq);
}
-long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb)
+static void epu_debug(struct cx18 *cx, struct cx18_epu_work_order *order)
{
- const struct cx18_api_info *info = find_api_info(mb->cmd);
- struct cx18_mailbox __iomem *ack_mb;
- u32 ack_irq;
- u8 rpu = CPU;
+ char *p;
+ char *str = order->str;
- if (info == NULL && mb->cmd) {
- CX18_WARN("Cannot ack unknown command %x\n", mb->cmd);
- return -EINVAL;
- }
- if (info)
- rpu = info->rpu;
+ CX18_DEBUG_INFO("%x %s\n", order->mb.args[0], str);
+ p = strchr(str, '.');
+ if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags) && p && p > str)
+ CX18_INFO("FW version: %s\n", p - 1);
+}
- switch (rpu) {
- case HPU:
- ack_irq = IRQ_EPU_TO_HPU_ACK;
- ack_mb = &cx->scb->hpu2epu_mb;
+static void epu_cmd(struct cx18 *cx, struct cx18_epu_work_order *order)
+{
+ switch (order->rpu) {
+ case CPU:
+ {
+ switch (order->mb.cmd) {
+ case CX18_EPU_DMA_DONE:
+ epu_dma_done(cx, order);
+ break;
+ case CX18_EPU_DEBUG:
+ epu_debug(cx, order);
+ break;
+ default:
+ CX18_WARN("Unknown CPU to EPU mailbox command %#0x\n",
+ order->mb.cmd);
+ break;
+ }
break;
+ }
+ case APU:
+ CX18_WARN("Unknown APU to EPU mailbox command %#0x\n",
+ order->mb.cmd);
+ break;
+ default:
+ break;
+ }
+}
+
+static
+void free_epu_work_order(struct cx18 *cx, struct cx18_epu_work_order *order)
+{
+ atomic_set(&order->pending, 0);
+}
+
+void cx18_epu_work_handler(struct work_struct *work)
+{
+ struct cx18_epu_work_order *order =
+ container_of(work, struct cx18_epu_work_order, work);
+ struct cx18 *cx = order->cx;
+ epu_cmd(cx, order);
+ free_epu_work_order(cx, order);
+}
+
+
+/*
+ * Functions that run in an interrupt handling context
+ */
+
+static void mb_ack_irq(struct cx18 *cx, struct cx18_epu_work_order *order)
+{
+ struct cx18_mailbox __iomem *ack_mb;
+ u32 ack_irq, req;
+
+ switch (order->rpu) {
case APU:
ack_irq = IRQ_EPU_TO_APU_ACK;
ack_mb = &cx->scb->apu2epu_mb;
@@ -170,26 +276,197 @@ long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb)
ack_mb = &cx->scb->cpu2epu_mb;
break;
default:
- CX18_WARN("Unknown RPU for command %x\n", mb->cmd);
- return -EINVAL;
+ CX18_WARN("Unhandled RPU (%d) for command %x ack\n",
+ order->rpu, order->mb.cmd);
+ return;
}
- cx18_setup_page(cx, SCB_OFFSET);
- cx18_write_sync(cx, mb->request, &ack_mb->ack);
+ req = order->mb.request;
+ /* Don't ack if the RPU has gotten impatient and timed us out */
+ if (req != cx18_readl(cx, &ack_mb->request) ||
+ req == cx18_readl(cx, &ack_mb->ack)) {
+ CX18_DEBUG_WARN("Possibly falling behind: %s self-ack'ed our "
+ "incoming %s to EPU mailbox (sequence no. %u) "
+ "while processing\n",
+ rpu_str[order->rpu], rpu_str[order->rpu], req);
+ order->flags |= CX18_F_EWO_MB_STALE_WHILE_PROC;
+ return;
+ }
+ cx18_writel(cx, req, &ack_mb->ack);
cx18_write_reg_expect(cx, ack_irq, SW2_INT_SET, ack_irq, ack_irq);
- return 0;
+ return;
+}
+
+static int epu_dma_done_irq(struct cx18 *cx, struct cx18_epu_work_order *order)
+{
+ u32 handle, mdl_ack_offset, mdl_ack_count;
+ struct cx18_mailbox *mb;
+
+ mb = &order->mb;
+ handle = mb->args[0];
+ mdl_ack_offset = mb->args[1];
+ mdl_ack_count = mb->args[2];
+
+ if (handle == CX18_INVALID_TASK_HANDLE ||
+ mdl_ack_count == 0 || mdl_ack_count > CX18_MAX_MDL_ACKS) {
+ if ((order->flags & CX18_F_EWO_MB_STALE) == 0)
+ mb_ack_irq(cx, order);
+ return -1;
+ }
+
+ cx18_memcpy_fromio(cx, order->mdl_ack, cx->enc_mem + mdl_ack_offset,
+ sizeof(struct cx18_mdl_ack) * mdl_ack_count);
+
+ if ((order->flags & CX18_F_EWO_MB_STALE) == 0)
+ mb_ack_irq(cx, order);
+ return 1;
+}
+
+static
+int epu_debug_irq(struct cx18 *cx, struct cx18_epu_work_order *order)
+{
+ u32 str_offset;
+ char *str = order->str;
+
+ str[0] = '\0';
+ str_offset = order->mb.args[1];
+ if (str_offset) {
+ cx18_setup_page(cx, str_offset);
+ cx18_memcpy_fromio(cx, str, cx->enc_mem + str_offset, 252);
+ str[252] = '\0';
+ cx18_setup_page(cx, SCB_OFFSET);
+ }
+
+ if ((order->flags & CX18_F_EWO_MB_STALE) == 0)
+ mb_ack_irq(cx, order);
+
+ return str_offset ? 1 : 0;
}
+static inline
+int epu_cmd_irq(struct cx18 *cx, struct cx18_epu_work_order *order)
+{
+ int ret = -1;
+
+ switch (order->rpu) {
+ case CPU:
+ {
+ switch (order->mb.cmd) {
+ case CX18_EPU_DMA_DONE:
+ ret = epu_dma_done_irq(cx, order);
+ break;
+ case CX18_EPU_DEBUG:
+ ret = epu_debug_irq(cx, order);
+ break;
+ default:
+ CX18_WARN("Unknown CPU to EPU mailbox command %#0x\n",
+ order->mb.cmd);
+ break;
+ }
+ break;
+ }
+ case APU:
+ CX18_WARN("Unknown APU to EPU mailbox command %#0x\n",
+ order->mb.cmd);
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+static inline
+struct cx18_epu_work_order *alloc_epu_work_order_irq(struct cx18 *cx)
+{
+ int i;
+ struct cx18_epu_work_order *order = NULL;
+
+ for (i = 0; i < CX18_MAX_EPU_WORK_ORDERS; i++) {
+ /*
+ * We only need "pending" atomic to inspect its contents,
+ * and need not do a check and set because:
+ * 1. Any work handler thread only clears "pending" and only
+ * on one, particular work order at a time, per handler thread.
+ * 2. "pending" is only set here, and we're serialized because
+ * we're called in an IRQ handler context.
+ */
+ if (atomic_read(&cx->epu_work_order[i].pending) == 0) {
+ order = &cx->epu_work_order[i];
+ atomic_set(&order->pending, 1);
+ break;
+ }
+ }
+ return order;
+}
+
+void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu)
+{
+ struct cx18_mailbox __iomem *mb;
+ struct cx18_mailbox *order_mb;
+ struct cx18_epu_work_order *order;
+ int submit;
+
+ switch (rpu) {
+ case CPU:
+ mb = &cx->scb->cpu2epu_mb;
+ break;
+ case APU:
+ mb = &cx->scb->apu2epu_mb;
+ break;
+ default:
+ return;
+ }
+
+ order = alloc_epu_work_order_irq(cx);
+ if (order == NULL) {
+ CX18_WARN("Unable to find blank work order form to schedule "
+ "incoming mailbox command processing\n");
+ return;
+ }
+
+ order->flags = 0;
+ order->rpu = rpu;
+ order_mb = &order->mb;
+
+ /* mb->cmd and mb->args[0] through mb->args[2] */
+ cx18_memcpy_fromio(cx, &order_mb->cmd, &mb->cmd, 4 * sizeof(u32));
+ /* mb->request and mb->ack. N.B. we want to read mb->ack last */
+ cx18_memcpy_fromio(cx, &order_mb->request, &mb->request,
+ 2 * sizeof(u32));
+
+ if (order_mb->request == order_mb->ack) {
+ CX18_DEBUG_WARN("Possibly falling behind: %s self-ack'ed our "
+ "incoming %s to EPU mailbox (sequence no. %u)"
+ "\n",
+ rpu_str[rpu], rpu_str[rpu], order_mb->request);
+ dump_mb(cx, order_mb, "incoming");
+ order->flags = CX18_F_EWO_MB_STALE_UPON_RECEIPT;
+ }
+
+ /*
+ * Individual EPU command processing is responsible for ack-ing
+ * a non-stale mailbox as soon as possible
+ */
+ submit = epu_cmd_irq(cx, order);
+ if (submit > 0) {
+ queue_work(cx->work_queue, &order->work);
+ }
+}
+
+
+/*
+ * Functions called from a non-interrupt, non work_queue context
+ */
static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
{
const struct cx18_api_info *info = find_api_info(cmd);
- u32 state = 0, irq = 0, req, oldreq, err;
+ u32 state, irq, req, ack, err;
struct cx18_mailbox __iomem *mb;
+ u32 __iomem *xpu_state;
wait_queue_head_t *waitq;
- int timeout = 100;
- int cnt = 0;
- int sig = 0;
+ struct mutex *mb_lock;
+ long int timeout, ret;
int i;
if (info == NULL) {
@@ -201,50 +478,104 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
CX18_DEBUG_HI_API("%s\n", info->name);
else
CX18_DEBUG_API("%s\n", info->name);
- cx18_setup_page(cx, SCB_OFFSET);
- mb = cx18_mb_is_complete(cx, info->rpu, &state, &irq, &req);
- if (mb == NULL) {
- CX18_ERR("mb %s busy\n", info->name);
- return -EBUSY;
+ switch (info->rpu) {
+ case APU:
+ waitq = &cx->mb_apu_waitq;
+ mb_lock = &cx->epu2apu_mb_lock;
+ irq = IRQ_EPU_TO_APU;
+ mb = &cx->scb->epu2apu_mb;
+ xpu_state = &cx->scb->apu_state;
+ break;
+ case CPU:
+ waitq = &cx->mb_cpu_waitq;
+ mb_lock = &cx->epu2cpu_mb_lock;
+ irq = IRQ_EPU_TO_CPU;
+ mb = &cx->scb->epu2cpu_mb;
+ xpu_state = &cx->scb->cpu_state;
+ break;
+ default:
+ CX18_WARN("Unknown RPU (%d) for API call\n", info->rpu);
+ return -EINVAL;
}
- oldreq = req - 1;
+ mutex_lock(mb_lock);
+ /*
+ * Wait for an in-use mailbox to complete
+ *
+ * If the XPU is responding with Ack's, the mailbox shouldn't be in
+ * a busy state, since we serialize access to it on our end.
+ *
+ * If the wait for ack after sending a previous command was interrupted
+ * by a signal, we may get here and find a busy mailbox. After waiting,
+ * mark it "not busy" from our end, if the XPU hasn't ack'ed it still.
+ */
+ state = cx18_readl(cx, xpu_state);
+ req = cx18_readl(cx, &mb->request);
+ timeout = msecs_to_jiffies(10);
+ ret = wait_event_timeout(*waitq,
+ (ack = cx18_readl(cx, &mb->ack)) == req,
+ timeout);
+ if (req != ack) {
+ /* waited long enough, make the mbox "not busy" from our end */
+ cx18_writel(cx, req, &mb->ack);
+ CX18_ERR("mbox was found stuck busy when setting up for %s; "
+ "clearing busy and trying to proceed\n", info->name);
+ } else if (ret != timeout)
+ CX18_DEBUG_API("waited %u msecs for busy mbox to be acked\n",
+ jiffies_to_msecs(timeout-ret));
+
+ /* Build the outgoing mailbox */
+ req = ((req & 0xfffffffe) == 0xfffffffe) ? 1 : req + 1;
+
cx18_writel(cx, cmd, &mb->cmd);
for (i = 0; i < args; i++)
cx18_writel(cx, data[i], &mb->args[i]);
cx18_writel(cx, 0, &mb->error);
cx18_writel(cx, req, &mb->request);
+ cx18_writel(cx, req - 1, &mb->ack); /* ensure ack & req are distinct */
- switch (info->rpu) {
- case APU: waitq = &cx->mb_apu_waitq; break;
- case CPU: waitq = &cx->mb_cpu_waitq; break;
- case EPU: waitq = &cx->mb_epu_waitq; break;
- case HPU: waitq = &cx->mb_hpu_waitq; break;
- default: return -EINVAL;
- }
- if (info->flags & API_FAST)
- timeout /= 2;
+ /*
+ * Notify the XPU and wait for it to send an Ack back
+ */
+ timeout = msecs_to_jiffies((info->flags & API_FAST) ? 10 : 20);
+
+ CX18_DEBUG_HI_IRQ("sending interrupt SW1: %x to send %s\n",
+ irq, info->name);
cx18_write_reg_expect(cx, irq, SW1_INT_SET, irq, irq);
- while (!sig && cx18_readl(cx, &mb->ack) != cx18_readl(cx, &mb->request)
- && cnt < 660) {
- if (cnt > 200 && !in_atomic())
- sig = cx18_msleep_timeout(10, 1);
- cnt++;
- }
- if (sig)
- return -EINTR;
- if (cnt == 660) {
- cx18_writel(cx, oldreq, &mb->request);
- CX18_ERR("mb %s failed\n", info->name);
+ ret = wait_event_timeout(
+ *waitq,
+ cx18_readl(cx, &mb->ack) == cx18_readl(cx, &mb->request),
+ timeout);
+
+ if (ret == 0) {
+ /* Timed out */
+ mutex_unlock(mb_lock);
+ CX18_DEBUG_WARN("sending %s timed out waiting %d msecs for RPU "
+ "acknowledgement\n",
+ info->name, jiffies_to_msecs(timeout));
return -EINVAL;
}
+
+ if (ret != timeout)
+ CX18_DEBUG_HI_API("waited %u msecs for %s to be acked\n",
+ jiffies_to_msecs(timeout-ret), info->name);
+
+ /* Collect data returned by the XPU */
for (i = 0; i < MAX_MB_ARGUMENTS; i++)
data[i] = cx18_readl(cx, &mb->args[i]);
err = cx18_readl(cx, &mb->error);
- if (!in_atomic() && (info->flags & API_SLOW))
+ mutex_unlock(mb_lock);
+
+ /*
+ * Wait for XPU to perform extra actions for the caller in some cases.
+ * e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all buffers
+ * back in a burst shortly thereafter
+ */
+ if (info->flags & API_SLOW)
cx18_msleep_timeout(300, 0);
+
if (err)
CX18_DEBUG_API("mailbox error %08x for command %s\n", err,
info->name);
@@ -253,12 +584,7 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[])
{
- int res = cx18_api_call(cx, cmd, args, data);
-
- /* Allow a single retry, probably already too late though.
- If there is no free mailbox then that is usually an indication
- of a more serious problem. */
- return (res == -EBUSY) ? cx18_api_call(cx, cmd, args, data) : res;
+ return cx18_api_call(cx, cmd, args, data);
}
static int cx18_set_filter_param(struct cx18_stream *s)
@@ -281,8 +607,9 @@ static int cx18_set_filter_param(struct cx18_stream *s)
int cx18_api_func(void *priv, u32 cmd, int in, int out,
u32 data[CX2341X_MBOX_MAX_DATA])
{
- struct cx18 *cx = priv;
- struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
+ struct cx18_api_func_private *api_priv = priv;
+ struct cx18 *cx = api_priv->cx;
+ struct cx18_stream *s = api_priv->s;
switch (cmd) {
case CX2341X_ENC_SET_OUTPUT_PORT:
diff --git a/drivers/media/video/cx18/cx18-mailbox.h b/drivers/media/video/cx18/cx18-mailbox.h
index d995641536b..ce2b6686aa0 100644
--- a/drivers/media/video/cx18/cx18-mailbox.h
+++ b/drivers/media/video/cx18/cx18-mailbox.h
@@ -2,6 +2,7 @@
* cx18 mailbox functions
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* 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
@@ -30,8 +31,24 @@
#define MB_RESERVED_HANDLE_0 0
#define MB_RESERVED_HANDLE_1 0xFFFFFFFF
+#define APU 0
+#define CPU 1
+#define EPU 2
+#define HPU 3
+
struct cx18;
+/*
+ * This structure is used by CPU to provide completed buffers information
+ * Its structure is dictrated by the layout of the SCB, required by the
+ * firmware, but its defintion needs to be here, instead of in cx18-scb.h,
+ * for mailbox work order scheduling
+ */
+struct cx18_mdl_ack {
+ u32 id; /* ID of a completed MDL */
+ u32 data_used; /* Total data filled in the MDL for buffer 'id' */
+};
+
/* The cx18_mailbox struct is the mailbox structure which is used for passing
messages between processors */
struct cx18_mailbox {
@@ -62,12 +79,22 @@ struct cx18_mailbox {
u32 error;
};
+struct cx18_stream;
+
+struct cx18_api_func_private {
+ struct cx18 *cx;
+ struct cx18_stream *s;
+};
+
int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]);
int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], u32 cmd,
int args, ...);
int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...);
int cx18_api_func(void *priv, u32 cmd, int in, int out,
u32 data[CX2341X_MBOX_MAX_DATA]);
-long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb);
+
+void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu);
+
+void cx18_epu_work_handler(struct work_struct *work);
#endif
diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c
index 174682c2582..8d9441e88c4 100644
--- a/drivers/media/video/cx18/cx18-queue.c
+++ b/drivers/media/video/cx18/cx18-queue.c
@@ -4,6 +4,7 @@
* Derived from ivtv-queue.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* 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
@@ -41,91 +42,126 @@ void cx18_queue_init(struct cx18_queue *q)
q->bytesused = 0;
}
-void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
- struct cx18_queue *q)
+struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
+ struct cx18_queue *q, int to_front)
{
- unsigned long flags = 0;
-
- /* clear the buffer if it is going to be enqueued to the free queue */
- if (q == &s->q_free) {
+ /* clear the buffer if it is not to be enqueued to the full queue */
+ if (q != &s->q_full) {
buf->bytesused = 0;
buf->readpos = 0;
buf->b_flags = 0;
+ buf->skipped = 0;
}
- spin_lock_irqsave(&s->qlock, flags);
- list_add_tail(&buf->list, &q->list);
- atomic_inc(&q->buffers);
+
+ mutex_lock(&s->qlock);
+
+ /* q_busy is restricted to a max buffer count imposed by firmware */
+ if (q == &s->q_busy &&
+ atomic_read(&q->buffers) >= CX18_MAX_FW_MDLS_PER_STREAM)
+ q = &s->q_free;
+
+ if (to_front)
+ list_add(&buf->list, &q->list); /* LIFO */
+ else
+ list_add_tail(&buf->list, &q->list); /* FIFO */
q->bytesused += buf->bytesused - buf->readpos;
- spin_unlock_irqrestore(&s->qlock, flags);
+ atomic_inc(&q->buffers);
+
+ mutex_unlock(&s->qlock);
+ return q;
}
struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q)
{
struct cx18_buffer *buf = NULL;
- unsigned long flags = 0;
- spin_lock_irqsave(&s->qlock, flags);
+ mutex_lock(&s->qlock);
if (!list_empty(&q->list)) {
- buf = list_entry(q->list.next, struct cx18_buffer, list);
- list_del_init(q->list.next);
- atomic_dec(&q->buffers);
+ buf = list_first_entry(&q->list, struct cx18_buffer, list);
+ list_del_init(&buf->list);
q->bytesused -= buf->bytesused - buf->readpos;
+ buf->skipped = 0;
+ atomic_dec(&q->buffers);
}
- spin_unlock_irqrestore(&s->qlock, flags);
+ mutex_unlock(&s->qlock);
return buf;
}
-struct cx18_buffer *cx18_queue_get_buf_irq(struct cx18_stream *s, u32 id,
+struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id,
u32 bytesused)
{
struct cx18 *cx = s->cx;
- struct list_head *p;
-
- spin_lock(&s->qlock);
- list_for_each(p, &s->q_free.list) {
- struct cx18_buffer *buf =
- list_entry(p, struct cx18_buffer, list);
-
- if (buf->id != id)
+ struct cx18_buffer *buf;
+ struct cx18_buffer *tmp;
+ struct cx18_buffer *ret = NULL;
+
+ mutex_lock(&s->qlock);
+ list_for_each_entry_safe(buf, tmp, &s->q_busy.list, list) {
+ if (buf->id != id) {
+ buf->skipped++;
+ if (buf->skipped >= atomic_read(&s->q_busy.buffers)-1) {
+ /* buffer must have fallen out of rotation */
+ CX18_WARN("Skipped %s, buffer %d, %d "
+ "times - it must have dropped out of "
+ "rotation\n", s->name, buf->id,
+ buf->skipped);
+ /* move it to q_free */
+ list_move_tail(&buf->list, &s->q_free.list);
+ buf->bytesused = buf->readpos = buf->b_flags =
+ buf->skipped = 0;
+ atomic_dec(&s->q_busy.buffers);
+ atomic_inc(&s->q_free.buffers);
+ }
continue;
+ }
buf->bytesused = bytesused;
- atomic_dec(&s->q_free.buffers);
- atomic_inc(&s->q_full.buffers);
- s->q_full.bytesused += buf->bytesused;
- list_move_tail(&buf->list, &s->q_full.list);
+ /* Sync the buffer before we release the qlock */
+ cx18_buf_sync_for_cpu(s, buf);
+ if (s->type == CX18_ENC_STREAM_TYPE_TS) {
+ /*
+ * TS doesn't use q_full. As we pull the buffer off of
+ * the queue here, the caller will have to put it back.
+ */
+ list_del_init(&buf->list);
+ } else {
+ /* Move buffer from q_busy to q_full */
+ list_move_tail(&buf->list, &s->q_full.list);
+ set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags);
+ s->q_full.bytesused += buf->bytesused;
+ atomic_inc(&s->q_full.buffers);
+ }
+ atomic_dec(&s->q_busy.buffers);
- spin_unlock(&s->qlock);
- return buf;
+ ret = buf;
+ break;
}
- spin_unlock(&s->qlock);
- CX18_ERR("Cannot find buffer %d for stream %s\n", id, s->name);
- return NULL;
+ mutex_unlock(&s->qlock);
+ return ret;
}
/* Move all buffers of a queue to q_free, while flushing the buffers */
static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q)
{
- unsigned long flags;
struct cx18_buffer *buf;
if (q == &s->q_free)
return;
- spin_lock_irqsave(&s->qlock, flags);
+ mutex_lock(&s->qlock);
while (!list_empty(&q->list)) {
- buf = list_entry(q->list.next, struct cx18_buffer, list);
- list_move_tail(q->list.next, &s->q_free.list);
- buf->bytesused = buf->readpos = buf->b_flags = 0;
+ buf = list_first_entry(&q->list, struct cx18_buffer, list);
+ list_move_tail(&buf->list, &s->q_free.list);
+ buf->bytesused = buf->readpos = buf->b_flags = buf->skipped = 0;
atomic_inc(&s->q_free.buffers);
}
cx18_queue_init(q);
- spin_unlock_irqrestore(&s->qlock, flags);
+ mutex_unlock(&s->qlock);
}
void cx18_flush_queues(struct cx18_stream *s)
{
- cx18_queue_flush(s, &s->q_io);
+ cx18_queue_flush(s, &s->q_busy);
cx18_queue_flush(s, &s->q_full);
}
diff --git a/drivers/media/video/cx18/cx18-queue.h b/drivers/media/video/cx18/cx18-queue.h
index 7f93bb13c09..456cec3bc28 100644
--- a/drivers/media/video/cx18/cx18-queue.h
+++ b/drivers/media/video/cx18/cx18-queue.h
@@ -4,6 +4,7 @@
* Derived from ivtv-queue.h
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* 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
@@ -42,11 +43,26 @@ static inline void cx18_buf_sync_for_device(struct cx18_stream *s,
void cx18_buf_swap(struct cx18_buffer *buf);
/* cx18_queue utility functions */
+struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
+ struct cx18_queue *q, int to_front);
+
+static inline
+struct cx18_queue *cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
+ struct cx18_queue *q)
+{
+ return _cx18_enqueue(s, buf, q, 0); /* FIFO */
+}
+
+static inline
+struct cx18_queue *cx18_push(struct cx18_stream *s, struct cx18_buffer *buf,
+ struct cx18_queue *q)
+{
+ return _cx18_enqueue(s, buf, q, 1); /* LIFO */
+}
+
void cx18_queue_init(struct cx18_queue *q);
-void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
- struct cx18_queue *q);
struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q);
-struct cx18_buffer *cx18_queue_get_buf_irq(struct cx18_stream *s, u32 id,
+struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id,
u32 bytesused);
void cx18_flush_queues(struct cx18_stream *s);
diff --git a/drivers/media/video/cx18/cx18-scb.c b/drivers/media/video/cx18/cx18-scb.c
index f56d3772aa6..34b4d03c55c 100644
--- a/drivers/media/video/cx18/cx18-scb.c
+++ b/drivers/media/video/cx18/cx18-scb.c
@@ -2,6 +2,7 @@
* cx18 System Control Block initialization
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* 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
@@ -117,6 +118,5 @@ void cx18_init_scb(struct cx18 *cx)
cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu_state),
&cx->scb->ipc_offset);
- cx18_writel(cx, 1, &cx->scb->hpu_state);
cx18_writel(cx, 1, &cx->scb->epu_state);
}
diff --git a/drivers/media/video/cx18/cx18-scb.h b/drivers/media/video/cx18/cx18-scb.h
index 594713bbed6..1dc1c431f5a 100644
--- a/drivers/media/video/cx18/cx18-scb.h
+++ b/drivers/media/video/cx18/cx18-scb.h
@@ -2,6 +2,7 @@
* cx18 System Control Block initialization
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* 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
@@ -85,12 +86,6 @@ struct cx18_mdl {
u32 length; /* Length of the buffer segment */
};
-/* This structure is used by CPU to provide completed buffers information */
-struct cx18_mdl_ack {
- u32 id; /* ID of a completed MDL */
- u32 data_used; /* Total data filled in the MDL for buffer 'id' */
-};
-
struct cx18_scb {
/* These fields form the System Control Block which is used at boot time
for localizing the IPC data as well as the code positions for all
@@ -276,7 +271,7 @@ struct cx18_scb {
struct cx18_mailbox hpu2epu_mb;
struct cx18_mailbox ppu2epu_mb;
- struct cx18_mdl_ack cpu_mdl_ack[CX18_MAX_STREAMS][2];
+ struct cx18_mdl_ack cpu_mdl_ack[CX18_MAX_STREAMS][CX18_MAX_MDL_ACKS];
struct cx18_mdl cpu_mdl[1];
};
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
index e5ff7705b7a..63c336c95ff 100644
--- a/drivers/media/video/cx18/cx18-streams.c
+++ b/drivers/media/video/cx18/cx18-streams.c
@@ -4,6 +4,7 @@
* Derived from ivtv-streams.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* 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
@@ -110,7 +111,6 @@ static void cx18_stream_init(struct cx18 *cx, int type)
{
struct cx18_stream *s = &cx->streams[type];
struct video_device *dev = s->v4l2dev;
- u32 max_size = cx->options.megabytes[type] * 1024 * 1024;
/* we need to keep v4l2dev, so restore it afterwards */
memset(s, 0, sizeof(*s));
@@ -123,21 +123,15 @@ static void cx18_stream_init(struct cx18 *cx, int type)
s->handle = CX18_INVALID_TASK_HANDLE;
s->dma = cx18_stream_info[type].dma;
+ s->buffers = cx->stream_buffers[type];
s->buf_size = cx->stream_buf_size[type];
- if (s->buf_size)
- s->buffers = max_size / s->buf_size;
- if (s->buffers > 63) {
- /* Each stream has a maximum of 63 buffers,
- ensure we do not exceed that. */
- s->buffers = 63;
- s->buf_size = (max_size / s->buffers) & ~0xfff;
- }
- spin_lock_init(&s->qlock);
+
+ mutex_init(&s->qlock);
init_waitqueue_head(&s->waitq);
s->id = -1;
cx18_queue_init(&s->q_free);
+ cx18_queue_init(&s->q_busy);
cx18_queue_init(&s->q_full);
- cx18_queue_init(&s->q_io);
}
static int cx18_prep_dev(struct cx18 *cx, int type)
@@ -167,7 +161,7 @@ static int cx18_prep_dev(struct cx18 *cx, int type)
/* User explicitly selected 0 buffers for these streams, so don't
create them. */
if (cx18_stream_info[type].dma != PCI_DMA_NONE &&
- cx->options.megabytes[type] == 0) {
+ cx->stream_buffers[type] == 0) {
CX18_INFO("Disabled %s device\n", cx18_stream_info[type].name);
return 0;
}
@@ -267,8 +261,9 @@ static int cx18_reg_dev(struct cx18 *cx, int type)
switch (vfl_type) {
case VFL_TYPE_GRABBER:
- CX18_INFO("Registered device video%d for %s (%d MB)\n",
- num, s->name, cx->options.megabytes[type]);
+ CX18_INFO("Registered device video%d for %s (%d x %d kB)\n",
+ num, s->name, cx->stream_buffers[type],
+ cx->stream_buf_size[type]/1024);
break;
case VFL_TYPE_RADIO:
@@ -277,10 +272,11 @@ static int cx18_reg_dev(struct cx18 *cx, int type)
break;
case VFL_TYPE_VBI:
- if (cx->options.megabytes[type])
- CX18_INFO("Registered device vbi%d for %s (%d MB)\n",
- num,
- s->name, cx->options.megabytes[type]);
+ if (cx->stream_buffers[type])
+ CX18_INFO("Registered device vbi%d for %s "
+ "(%d x %d bytes)\n",
+ num, s->name, cx->stream_buffers[type],
+ cx->stream_buf_size[type]);
else
CX18_INFO("Registered device vbi%d for %s\n",
num, s->name);
@@ -344,7 +340,7 @@ void cx18_streams_cleanup(struct cx18 *cx, int unregister)
static void cx18_vbi_setup(struct cx18_stream *s)
{
struct cx18 *cx = s->cx;
- int raw = cx->vbi.sliced_in->service_set == 0;
+ int raw = cx18_raw_vbi(cx);
u32 data[CX2341X_MBOX_MAX_DATA];
int lines;
@@ -362,8 +358,7 @@ static void cx18_vbi_setup(struct cx18_stream *s)
cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in);
/* determine number of lines and total number of VBI bytes.
- A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1
- The '- 1' byte is probably an unused U or V byte. Or something...
+ A raw line takes 1444 bytes: 4 byte SAV code + 2 * 720
A sliced line takes 51 bytes: 4 byte frame header, 4 byte internal
header, 42 data bytes + checksum (to be confirmed) */
if (raw) {
@@ -381,14 +376,15 @@ static void cx18_vbi_setup(struct cx18_stream *s)
/* Lines per field */
data[1] = (lines / 2) | ((lines / 2) << 16);
/* bytes per line */
- data[2] = (raw ? cx->vbi.raw_size : cx->vbi.sliced_size);
+ data[2] = (raw ? cx->vbi.raw_decoder_line_size
+ : cx->vbi.sliced_decoder_line_size);
/* Every X number of frames a VBI interrupt arrives
(frames as in 25 or 30 fps) */
data[3] = 1;
/* Setup VBI for the cx25840 digitizer */
if (raw) {
data[4] = 0x20602060;
- data[5] = 0x30703070;
+ data[5] = 0x307090d0;
} else {
data[4] = 0xB0F0B0F0;
data[5] = 0xA0E0A0E0;
@@ -401,11 +397,52 @@ static void cx18_vbi_setup(struct cx18_stream *s)
cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data);
}
+struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s,
+ struct cx18_buffer *buf)
+{
+ struct cx18 *cx = s->cx;
+ struct cx18_queue *q;
+
+ /* Don't give it to the firmware, if we're not running a capture */
+ if (s->handle == CX18_INVALID_TASK_HANDLE ||
+ !test_bit(CX18_F_S_STREAMING, &s->s_flags))
+ return cx18_enqueue(s, buf, &s->q_free);
+
+ q = cx18_enqueue(s, buf, &s->q_busy);
+ if (q != &s->q_busy)
+ return q; /* The firmware has the max buffers it can handle */
+
+ cx18_buf_sync_for_device(s, buf);
+ cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
+ (void __iomem *) &cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
+ 1, buf->id, s->buf_size);
+ return q;
+}
+
+void cx18_stream_load_fw_queue(struct cx18_stream *s)
+{
+ struct cx18_queue *q;
+ struct cx18_buffer *buf;
+
+ if (atomic_read(&s->q_free.buffers) == 0 ||
+ atomic_read(&s->q_busy.buffers) >= CX18_MAX_FW_MDLS_PER_STREAM)
+ return;
+
+ /* Move from q_free to q_busy notifying the firmware, until the limit */
+ do {
+ buf = cx18_dequeue(s, &s->q_free);
+ if (buf == NULL)
+ break;
+ q = cx18_stream_put_buf_fw(s, buf);
+ } while (atomic_read(&s->q_busy.buffers) < CX18_MAX_FW_MDLS_PER_STREAM
+ && q == &s->q_busy);
+}
+
int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
{
u32 data[MAX_MB_ARGUMENTS];
struct cx18 *cx = s->cx;
- struct list_head *p;
+ struct cx18_buffer *buf;
int ts = 0;
int captype = 0;
@@ -434,8 +471,8 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
captype = CAPTURE_CHANNEL_TYPE_PCM;
break;
case CX18_ENC_STREAM_TYPE_VBI:
- captype = cx->vbi.sliced_in->service_set ?
- CAPTURE_CHANNEL_TYPE_SLICED_VBI : CAPTURE_CHANNEL_TYPE_VBI;
+ captype = cx18_raw_vbi(cx) ?
+ CAPTURE_CHANNEL_TYPE_VBI : CAPTURE_CHANNEL_TYPE_SLICED_VBI;
cx->vbi.frame = 0;
cx->vbi.inserted_frame = 0;
memset(cx->vbi.sliced_mpeg_size,
@@ -457,6 +494,8 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
cx18_vapi(cx, CX18_CPU_SET_CHANNEL_TYPE, 2, s->handle, captype);
if (atomic_read(&cx->ana_capturing) == 0 && !ts) {
+ struct cx18_api_func_private priv;
+
/* Stuff from Windows, we don't know what it is */
cx18_vapi(cx, CX18_CPU_SET_VER_CROP_LINE, 2, s->handle, 0);
cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 3, 1);
@@ -476,7 +515,9 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 1, 0);
/* Setup API for Stream */
- cx2341x_update(cx, cx18_api_func, NULL, &cx->params);
+ priv.cx = cx;
+ priv.s = s;
+ cx2341x_update(&priv, cx18_api_func, NULL, &cx->params);
}
if (atomic_read(&cx->tot_capturing) == 0) {
@@ -488,16 +529,17 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
(void __iomem *)&cx->scb->cpu_mdl_ack[s->type][0] - cx->enc_mem,
(void __iomem *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem);
- list_for_each(p, &s->q_free.list) {
- struct cx18_buffer *buf = list_entry(p, struct cx18_buffer, list);
-
+ /* Init all the cpu_mdls for this stream */
+ cx18_flush_queues(s);
+ mutex_lock(&s->qlock);
+ list_for_each_entry(buf, &s->q_free.list, list) {
cx18_writel(cx, buf->dma_handle,
&cx->scb->cpu_mdl[buf->id].paddr);
cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length);
- cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
- (void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
- 1, buf->id, s->buf_size);
}
+ mutex_unlock(&s->qlock);
+ cx18_stream_load_fw_queue(s);
+
/* begin_capture */
if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) {
CX18_DEBUG_WARN("Error starting capture!\n");
@@ -506,9 +548,15 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, 1);
else
cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle);
+ clear_bit(CX18_F_S_STREAMING, &s->s_flags);
+ /* FIXME - CX18_F_S_STREAMOFF as well? */
cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle);
cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
- /* FIXME - clean-up DSP0_INT mask, i_flags, s_flags, etc. */
+ s->handle = CX18_INVALID_TASK_HANDLE;
+ if (atomic_read(&cx->tot_capturing) == 0) {
+ set_bit(CX18_F_I_EOS, &cx->i_flags);
+ cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK);
+ }
return -EINVAL;
}
@@ -560,9 +608,6 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end)
CX18_INFO("ignoring gop_end: not (yet?) supported by the firmware\n");
}
- /* Tell the CX23418 it can't use our buffers anymore */
- cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle);
-
if (s->type != CX18_ENC_STREAM_TYPE_TS)
atomic_dec(&cx->ana_capturing);
atomic_dec(&cx->tot_capturing);
@@ -570,6 +615,9 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end)
/* Clear capture and no-read bits */
clear_bit(CX18_F_S_STREAMING, &s->s_flags);
+ /* Tell the CX23418 it can't use our buffers anymore */
+ cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle);
+
cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
s->handle = CX18_INVALID_TASK_HANDLE;
@@ -595,3 +643,21 @@ u32 cx18_find_handle(struct cx18 *cx)
}
return CX18_INVALID_TASK_HANDLE;
}
+
+struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle)
+{
+ int i;
+ struct cx18_stream *s;
+
+ if (handle == CX18_INVALID_TASK_HANDLE)
+ return NULL;
+
+ for (i = 0; i < CX18_MAX_STREAMS; i++) {
+ s = &cx->streams[i];
+ if (s->handle != handle)
+ continue;
+ if (s->v4l2dev || s->dvb.enabled)
+ return s;
+ }
+ return NULL;
+}
diff --git a/drivers/media/video/cx18/cx18-streams.h b/drivers/media/video/cx18/cx18-streams.h
index f327e947b24..420e0a17294 100644
--- a/drivers/media/video/cx18/cx18-streams.h
+++ b/drivers/media/video/cx18/cx18-streams.h
@@ -4,6 +4,7 @@
* Derived from ivtv-streams.h
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* 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
@@ -22,11 +23,15 @@
*/
u32 cx18_find_handle(struct cx18 *cx);
+struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle);
int cx18_streams_setup(struct cx18 *cx);
int cx18_streams_register(struct cx18 *cx);
void cx18_streams_cleanup(struct cx18 *cx, int unregister);
/* Capture related */
+void cx18_stream_load_fw_queue(struct cx18_stream *s);
+struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s,
+ struct cx18_buffer *buf);
int cx18_start_v4l2_encode_stream(struct cx18_stream *s);
int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end);
diff --git a/drivers/media/video/cx18/cx18-vbi.c b/drivers/media/video/cx18/cx18-vbi.c
index 22e76ee3f44..fb595bd548e 100644
--- a/drivers/media/video/cx18/cx18-vbi.c
+++ b/drivers/media/video/cx18/cx18-vbi.c
@@ -160,11 +160,14 @@ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf,
return;
/* Raw VBI data */
- if (cx->vbi.sliced_in->service_set == 0) {
+ if (cx18_raw_vbi(cx)) {
u8 type;
cx18_buf_swap(buf);
+ /* Skip 12 bytes of header that gets stuffed in */
+ size -= 12;
+ memcpy(p, &buf->buf[12], size);
type = p[3];
size = buf->bytesused = compress_raw_buf(cx, p, size);
diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h
index 9f6be2d457f..84c0ff13b60 100644
--- a/drivers/media/video/cx18/cx18-version.h
+++ b/drivers/media/video/cx18/cx18-version.h
@@ -25,7 +25,7 @@
#define CX18_DRIVER_NAME "cx18"
#define CX18_DRIVER_VERSION_MAJOR 1
#define CX18_DRIVER_VERSION_MINOR 0
-#define CX18_DRIVER_VERSION_PATCHLEVEL 1
+#define CX18_DRIVER_VERSION_PATCHLEVEL 4
#define CX18_VERSION __stringify(CX18_DRIVER_VERSION_MAJOR) "." __stringify(CX18_DRIVER_VERSION_MINOR) "." __stringify(CX18_DRIVER_VERSION_PATCHLEVEL)
#define CX18_DRIVER_VERSION KERNEL_VERSION(CX18_DRIVER_VERSION_MAJOR, \
diff --git a/drivers/media/video/cx18/cx23418.h b/drivers/media/video/cx18/cx23418.h
index 668f968d776..601f3a2ab74 100644
--- a/drivers/media/video/cx18/cx23418.h
+++ b/drivers/media/video/cx18/cx23418.h
@@ -44,6 +44,7 @@
/* All commands for CPU have the following mask set */
#define CPU_CMD_MASK 0x20000000
+#define CPU_CMD_MASK_DEBUG (CPU_CMD_MASK | 0x00000000)
#define CPU_CMD_MASK_ACK (CPU_CMD_MASK | 0x80000000)
#define CPU_CMD_MASK_CAPTURE (CPU_CMD_MASK | 0x00020000)
#define CPU_CMD_MASK_TS (CPU_CMD_MASK | 0x00040000)
@@ -71,6 +72,11 @@
0/zero/NULL means "I have nothing to say" */
#define CX18_EPU_DEBUG (EPU_CMD_MASK_DEBUG | 0x0003)
+/* Reads memory/registers (32-bit)
+ IN[0] - Address
+ OUT[1] - Value */
+#define CX18_CPU_DEBUG_PEEK32 (CPU_CMD_MASK_DEBUG | 0x0003)
+
/* Description: This command starts streaming with the set channel type
IN[0] - Task handle. Handle of the task to start
ReturnCode - One of the ERR_CAPTURE_... */