summaryrefslogtreecommitdiff
path: root/linux/drivers/media/video/cx18
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab@redhat.com>2009-01-02 09:35:27 -0200
committerMauro Carvalho Chehab <mchehab@redhat.com>2009-01-02 09:35:27 -0200
commitc036738278216aac0276743b0eb1e88e5cfd1f25 (patch)
tree3415ee49f4624d59d60d3b39e0745315a8366426 /linux/drivers/media/video/cx18
parentfc7012444ff33c182360e0bade20bd56e2a42631 (diff)
parent70e0ec035b78cab8338a5b20518bfc1d1307b7ad (diff)
downloadmediapointer-dvb-s2-c036738278216aac0276743b0eb1e88e5cfd1f25.tar.gz
mediapointer-dvb-s2-c036738278216aac0276743b0eb1e88e5cfd1f25.tar.bz2
merge: http://linuxtv.org/hg/~mkrufky/tiger
From: Mauro Carvalho Chehab <mchehab@redhat.com> Priority: normal Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'linux/drivers/media/video/cx18')
-rw-r--r--linux/drivers/media/video/cx18/Kconfig2
-rw-r--r--linux/drivers/media/video/cx18/cx18-av-audio.c189
-rw-r--r--linux/drivers/media/video/cx18/cx18-av-core.c2
-rw-r--r--linux/drivers/media/video/cx18/cx18-av-vbi.c5
-rw-r--r--linux/drivers/media/video/cx18/cx18-cards.c8
-rw-r--r--linux/drivers/media/video/cx18/cx18-cards.h5
-rw-r--r--linux/drivers/media/video/cx18/cx18-controls.c5
-rw-r--r--linux/drivers/media/video/cx18/cx18-driver.c258
-rw-r--r--linux/drivers/media/video/cx18/cx18-driver.h27
-rw-r--r--linux/drivers/media/video/cx18/cx18-dvb.c4
-rw-r--r--linux/drivers/media/video/cx18/cx18-fileops.c43
-rw-r--r--linux/drivers/media/video/cx18/cx18-fileops.h4
-rw-r--r--linux/drivers/media/video/cx18/cx18-firmware.c170
-rw-r--r--linux/drivers/media/video/cx18/cx18-i2c.c28
-rw-r--r--linux/drivers/media/video/cx18/cx18-i2c.h1
-rw-r--r--linux/drivers/media/video/cx18/cx18-io.h6
-rw-r--r--linux/drivers/media/video/cx18/cx18-ioctl.c63
-rw-r--r--linux/drivers/media/video/cx18/cx18-ioctl.h2
-rw-r--r--linux/drivers/media/video/cx18/cx18-mailbox.c36
-rw-r--r--linux/drivers/media/video/cx18/cx18-mailbox.h7
-rw-r--r--linux/drivers/media/video/cx18/cx18-queue.c82
-rw-r--r--linux/drivers/media/video/cx18/cx18-queue.h19
-rw-r--r--linux/drivers/media/video/cx18/cx18-scb.c1
-rw-r--r--linux/drivers/media/video/cx18/cx18-streams.c126
-rw-r--r--linux/drivers/media/video/cx18/cx18-streams.h3
-rw-r--r--linux/drivers/media/video/cx18/cx18-vbi.c5
-rw-r--r--linux/drivers/media/video/cx18/cx18-version.h2
-rw-r--r--linux/drivers/media/video/cx18/cx23418.h6
28 files changed, 739 insertions, 370 deletions
diff --git a/linux/drivers/media/video/cx18/Kconfig b/linux/drivers/media/video/cx18/Kconfig
index ef48565de..8940b5387 100644
--- a/linux/drivers/media/video/cx18/Kconfig
+++ b/linux/drivers/media/video/cx18/Kconfig
@@ -9,7 +9,7 @@ config VIDEO_CX18
select VIDEO_CX2341X
select VIDEO_CS5345
select DVB_S5H1409 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_MXL5005S if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMIZE
---help---
This is a video4linux driver for Conexant cx23418 based
PCI combo video recorder devices.
diff --git a/linux/drivers/media/video/cx18/cx18-av-audio.c b/linux/drivers/media/video/cx18/cx18-av-audio.c
index fd85b9b2d..a2f0ad570 100644
--- a/linux/drivers/media/video/cx18/cx18-av-audio.c
+++ b/linux/drivers/media/video/cx18/cx18-av-audio.c
@@ -31,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);
+
+ /* VID_PLL Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+ cx18_av_write4(cx, 0x10c, 0x002be2fe);
- /* AUX_PLL_FRAC */
- /* 0x8.9504318a * 28,636,363.636 / 0x14 = 32000 * 384 */
- cx18_av_write4(cx, 0x110, 0x012a0863);
+ /* 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 */
@@ -134,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);
+
+ /* VID_PLL Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+ cx18_av_write4(cx, 0x10c, 0x002be2fe);
- /* AUX_PLL_FRAC */
- /* 0x9.7635e74 * 28,636,363.63 / 0x18 = 44100 * 256 */
- cx18_av_write4(cx, 0x110, 0x00ec6bce);
+ /* 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 */
@@ -166,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);
- /* AUX_PLL_FRAC */
- /* 0xa.4c6b6ea * 28,636,363.63 / 0x18 = 48000 * 256 */
- 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 = 0x176740c */
+ /* xtal * 0xd.bb3a060/0x20 = 48000 * 256: 393 MHz p-pd*/
+ cx18_av_write4(cx, 0x110, 0x0176740c);
/* src1_ctl */
/* 0x1.8000 = 48000/32000 */
@@ -195,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;
}
}
diff --git a/linux/drivers/media/video/cx18/cx18-av-core.c b/linux/drivers/media/video/cx18/cx18-av-core.c
index 40ea6fde6..0b1c84b4d 100644
--- a/linux/drivers/media/video/cx18/cx18-av-core.c
+++ b/linux/drivers/media/video/cx18/cx18-av-core.c
@@ -271,7 +271,7 @@ void cx18_av_std_setup(struct cx18 *cx)
if (pll_post) {
int fin, fsc, pll;
- pll = (28636364L * ((((u64)pll_int) << 25) + pll_frac)) >> 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);
diff --git a/linux/drivers/media/video/cx18/cx18-av-vbi.c b/linux/drivers/media/video/cx18/cx18-av-vbi.c
index 02fdf57bb..1527ea4f6 100644
--- a/linux/drivers/media/video/cx18/cx18-av-vbi.c
+++ b/linux/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/linux/drivers/media/video/cx18/cx18-cards.c b/linux/drivers/media/video/cx18/cx18-cards.c
index 23bd871b7..10cddba3e 100644
--- a/linux/drivers/media/video/cx18/cx18-cards.c
+++ b/linux/drivers/media/video/cx18/cx18-cards.c
@@ -51,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,
@@ -97,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,
@@ -152,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,
@@ -249,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/linux/drivers/media/video/cx18/cx18-cards.h b/linux/drivers/media/video/cx18/cx18-cards.h
index a54aae9ed..6fa7bcb42 100644
--- a/linux/drivers/media/video/cx18/cx18-cards.h
+++ b/linux/drivers/media/video/cx18/cx18-cards.h
@@ -48,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/linux/drivers/media/video/cx18/cx18-controls.c b/linux/drivers/media/video/cx18/cx18-controls.c
index f46c7e5ed..17edf305d 100644
--- a/linux/drivers/media/video/cx18/cx18-controls.c
+++ b/linux/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/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c
index 1fa9a670b..e447b4ce7 100644
--- a/linux/drivers/media/video/cx18/cx18-driver.c
+++ b/linux/drivers/media/video/cx18/cx18-driver.c
@@ -56,9 +56,6 @@ struct cx18 *cx18_cards[CX18_MAX_CARDS];
/* Protects cx18_cards_active */
DEFINE_SPINLOCK(cx18_cards_lock);
-/* Queue for deferrable IRQ handling work for all cx18 cards in system */
-struct workqueue_struct *cx18_work_queue;
-
/* add your revision and whatnot here */
static struct pci_device_id cx18_pci_tbl[] __devinitdata = {
{PCI_VENDOR_ID_CX, PCI_DEVICE_ID_CX23418,
@@ -86,12 +83,28 @@ 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;
static int mmio_ndelay;
@@ -111,12 +124,27 @@ 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,
@@ -157,21 +185,57 @@ MODULE_PARM_DESC(retry_mmio,
MODULE_PARM_DESC(mmio_ndelay,
"(Deprecated) MMIO accesses are now never purposely delayed\n"
"\t\t\tEffectively: 0 ns");
-MODULE_PARM_DESC(enc_mpg_buffers,
- "Encoder MPG Buffers (in MB)\n"
- "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS));
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");
@@ -364,11 +428,65 @@ 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];
@@ -446,6 +564,12 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)
spin_lock_init(&cx->lock);
+ cx->work_queue = create_singlethread_workqueue(cx->name);
+ if (cx->work_queue == NULL) {
+ CX18_ERR("Unable to create work hander thread\n");
+ return -ENOMEM;
+ }
+
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;
@@ -475,15 +599,52 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)
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;
}
@@ -516,6 +677,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;
}
@@ -660,12 +822,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);
@@ -724,6 +883,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,
@@ -773,13 +933,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;
@@ -835,6 +1005,7 @@ free_map:
free_mem:
release_mem_region(cx->base_addr, CX18_MEM_SIZE);
free_workqueue:
+ destroy_workqueue(cx->work_queue);
err:
if (retval == 0)
retval = -ENODEV;
@@ -917,6 +1088,7 @@ int cx18_init_on_first_open(struct cx18 *cx)
return 0;
}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
static void cx18_cancel_epu_work_orders(struct cx18 *cx)
{
int i;
@@ -924,6 +1096,7 @@ static void cx18_cancel_epu_work_orders(struct cx18 *cx)
cancel_work_sync(&cx->epu_work_order[i].work);
}
+#endif
static void cx18_remove(struct pci_dev *pci_dev)
{
struct cx18 *cx = pci_get_drvdata(pci_dev);
@@ -937,11 +1110,22 @@ static void cx18_remove(struct pci_dev *pci_dev)
/* Interrupts */
cx18_sw1_irq_disable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
cx18_halt_firmware(cx);
cx18_cancel_epu_work_orders(cx);
+#else
+
+ flush_workqueue(cx->work_queue);
+
+ cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
+
+ cx18_halt_firmware(cx);
+#endif
+
+ destroy_workqueue(cx->work_queue);
cx18_streams_cleanup(cx, 1);
@@ -984,17 +1168,8 @@ static int module_start(void)
printk(KERN_INFO "cx18: Debug value must be >= 0 and <= 511!\n");
}
- cx18_work_queue = create_singlethread_workqueue("cx18");
- if (cx18_work_queue == NULL) {
- printk(KERN_ERR
- "cx18: Unable to create work hander thread\n");
- return -ENOMEM;
- }
-
if (pci_register_driver(&cx18_pci_driver)) {
printk(KERN_ERR "cx18: Error detecting PCI card\n");
- destroy_workqueue(cx18_work_queue);
- cx18_work_queue = NULL;
return -ENODEV;
}
printk(KERN_INFO "cx18: End initialization\n");
@@ -1007,9 +1182,6 @@ static void module_cleanup(void)
pci_unregister_driver(&cx18_pci_driver);
- destroy_workqueue(cx18_work_queue);
- cx18_work_queue = NULL;
-
for (i = 0; i < cx18_cards_active; i++) {
if (cx18_cards[i] == NULL)
continue;
diff --git a/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h
index ca1f43781..0d2edebc3 100644
--- a/linux/drivers/media/video/cx18/cx18-driver.h
+++ b/linux/drivers/media/video/cx18/cx18-driver.h
@@ -115,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
@@ -244,7 +255,8 @@ struct cx18_scb; /* forward reference */
#define CX18_MAX_MDL_ACKS 2
-#define CX18_MAX_EPU_WORK_ORDERS 70 /* CPU_DE_RELEASE_MDL bursts 63 commands */
+#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
@@ -289,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;
@@ -408,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 */
@@ -447,6 +460,7 @@ struct cx18 {
u32 sw2_irq_mask;
u32 hw2_irq_mask;
+ struct workqueue_struct *work_queue;
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 */
@@ -478,7 +492,6 @@ extern struct cx18 *cx18_cards[];
extern int cx18_cards_active;
extern int cx18_first_minor;
extern spinlock_t cx18_cards_lock;
-extern struct workqueue_struct *cx18_work_queue;
/*==============Prototypes==================*/
@@ -492,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/linux/drivers/media/video/cx18/cx18-dvb.c b/linux/drivers/media/video/cx18/cx18-dvb.c
index 034e09a37..bd5e6f3fd 100644
--- a/linux/drivers/media/video/cx18/cx18-dvb.c
+++ b/linux/drivers/media/video/cx18/cx18-dvb.c
@@ -217,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;
diff --git a/linux/drivers/media/video/cx18/cx18-fileops.c b/linux/drivers/media/video/cx18/cx18-fileops.c
index 45b5f402e..0aaea0e7f 100644
--- a/linux/drivers/media/video/cx18/cx18-fileops.c
+++ b/linux/drivers/media/video/cx18/cx18-fileops.c
@@ -67,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;
@@ -224,8 +223,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;
@@ -233,11 +234,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) {
@@ -301,7 +297,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;
@@ -376,8 +372,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 (;;) {
@@ -404,16 +399,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;
@@ -557,7 +546,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;
@@ -601,7 +590,7 @@ void cx18_stop_capture(struct cx18_open_id *id, int gop_end)
}
}
-int cx18_v4l2_close(struct inode *inode, struct file *filp)
+int cx18_v4l2_close(struct file *filp)
{
struct cx18_open_id *id = filp->private_data;
struct cx18 *cx = id->cx;
@@ -699,12 +688,12 @@ static int cx18_serialized_open(struct cx18_stream *s, struct file *filp)
return 0;
}
-int cx18_v4l2_open(struct inode *inode, struct file *filp)
+int cx18_v4l2_open(struct file *filp)
{
int res, x, y = 0;
struct cx18 *cx = NULL;
struct cx18_stream *s = NULL;
- int minor = iminor(inode);
+ int minor = video_devdata(filp)->minor;
/* Find which card this open was on */
spin_lock(&cx18_cards_lock);
diff --git a/linux/drivers/media/video/cx18/cx18-fileops.h b/linux/drivers/media/video/cx18/cx18-fileops.h
index 46da0282f..92e2d5dab 100644
--- a/linux/drivers/media/video/cx18/cx18-fileops.h
+++ b/linux/drivers/media/video/cx18/cx18-fileops.h
@@ -22,12 +22,12 @@
*/
/* Testing/Debugging */
-int cx18_v4l2_open(struct inode *inode, struct file *filp);
+int cx18_v4l2_open(struct file *filp);
ssize_t cx18_v4l2_read(struct file *filp, char __user *buf, size_t count,
loff_t *pos);
ssize_t cx18_v4l2_write(struct file *filp, const char __user *buf, size_t count,
loff_t *pos);
-int cx18_v4l2_close(struct inode *inode, struct file *filp);
+int cx18_v4l2_close(struct file *filp);
unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait);
int cx18_start_capture(struct cx18_open_id *id);
void cx18_stop_capture(struct cx18_open_id *id, int gop_end);
diff --git a/linux/drivers/media/video/cx18/cx18-firmware.c b/linux/drivers/media/video/cx18/cx18-firmware.c
index 8eac84314..1fa95da15 100644
--- a/linux/drivers/media/video/cx18/cx18-firmware.c
+++ b/linux/drivers/media/video/cx18/cx18-firmware.c
@@ -26,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
@@ -224,7 +225,45 @@ void cx18_init_power(struct cx18 *cx, int lowpwr)
cx18_write_reg_expect(cx, 0x00020000, CX18_ADEC_CONTROL,
0x00000000, 0x00020002);
- /* The fast clock is at 200/245 MHz */
+ /*
+ * 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);
@@ -234,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 */
@@ -332,6 +391,10 @@ 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);
@@ -341,65 +404,62 @@ int cx18_firmware_init(struct cx18 *cx)
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) {
- u32 fw_entry_addr = 0;
- int sz = load_apu_fw_direct("v4l-cx23418-apu.fw",
- cx->enc_mem, cx, &fw_entry_addr);
-
- if (sz <= 0)
- return sz;
-
- /* Clear bit0 for APU to start from 0 */
- cx18_write_reg(cx, cx18_read_reg(cx, 0xc72030) & ~1, 0xc72030);
-
- cx18_write_enc(cx, 0xE51FF004, 0); /* ldr pc, [pc, #-4] */
- cx18_write_enc(cx, fw_entry_addr, 4);
-
- /* Start APU */
- cx18_write_reg_expect(cx, 0x00010000, CX18_PROC_SOFT_RESET,
- 0x00000000, 0x00010001);
- 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_expect(cx,
- 0x00080000, CX18_PROC_SOFT_RESET,
- 0x00000000, 0x00080008);
- 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 firmware apparently sets up to receive an interrupt for it's
- * outgoing IRQ_CPU_TO_EPU_ACK to us (*boggle*). 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 behavior by clearing the CPU's
- * interrupt enables for Ack IRQ's we want to process.
+ * 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_expect(cx, 0x14001400, 0xc78110, 0x00001400, 0x14001400);
return 0;
diff --git a/linux/drivers/media/video/cx18/cx18-i2c.c b/linux/drivers/media/video/cx18/cx18-i2c.c
index c0a79ecd4..efae2301a 100644
--- a/linux/drivers/media/video/cx18/cx18-i2c.c
+++ b/linux/drivers/media/video/cx18/cx18-i2c.c
@@ -268,7 +268,7 @@ int cx18_call_i2c_client(struct cx18 *cx, int addr, unsigned cmd, void *arg)
return retval;
}
}
- if (cmd != VIDIOC_G_CHIP_IDENT)
+ if (cmd != VIDIOC_DBG_G_CHIP_IDENT)
CX18_ERR("i2c addr 0x%02x not found for cmd 0x%x!\n",
addr, cmd);
return -ENODEV;
@@ -294,17 +294,6 @@ static int cx18_i2c_id_addr(struct cx18 *cx, u32 id)
return retval;
}
-/* Find the i2c device name matching the DRIVERID */
-static const char *cx18_i2c_id_name(u32 id)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
- if (hw_driverids[i] == id)
- return hw_devicenames[i];
- return "unknown device";
-}
-
/* Find the i2c device name matching the CX18_HW_ flag */
static const char *cx18_i2c_hw_name(u32 hw)
{
@@ -352,21 +341,6 @@ int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg)
return cx18_call_i2c_client(cx, addr, cmd, arg);
}
-/* Calls i2c device based on I2C driver ID. */
-int cx18_i2c_id(struct cx18 *cx, u32 id, unsigned int cmd, void *arg)
-{
- int addr;
-
- addr = cx18_i2c_id_addr(cx, id);
- if (addr < 0) {
- if (cmd != VIDIOC_G_CHIP_IDENT)
- CX18_ERR("i2c ID 0x%08x (%s) not found for cmd 0x%x!\n",
- id, cx18_i2c_id_name(id), cmd);
- return addr;
- }
- return cx18_call_i2c_client(cx, addr, cmd, arg);
-}
-
/* broadcast cmd for all I2C clients and for the gpio subsystem */
void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg)
{
diff --git a/linux/drivers/media/video/cx18/cx18-i2c.h b/linux/drivers/media/video/cx18/cx18-i2c.h
index 113c3f9a2..486973901 100644
--- a/linux/drivers/media/video/cx18/cx18-i2c.h
+++ b/linux/drivers/media/video/cx18/cx18-i2c.h
@@ -23,7 +23,6 @@
int cx18_i2c_hw_addr(struct cx18 *cx, u32 hw);
int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg);
-int cx18_i2c_id(struct cx18 *cx, u32 id, unsigned int cmd, void *arg);
int cx18_call_i2c_client(struct cx18 *cx, int addr, unsigned cmd, void *arg);
void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg);
int cx18_i2c_register(struct cx18 *cx, unsigned idx);
diff --git a/linux/drivers/media/video/cx18/cx18-io.h b/linux/drivers/media/video/cx18/cx18-io.h
index e6716dcb1..2635b3a8c 100644
--- a/linux/drivers/media/video/cx18/cx18-io.h
+++ b/linux/drivers/media/video/cx18/cx18-io.h
@@ -83,10 +83,14 @@ void cx18_writel_expect(struct cx18 *cx, u32 val, void __iomem *addr,
u32 eval, u32 mask)
{
int i;
+ u32 r;
eval &= mask;
for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) {
cx18_writel_noretry(cx, val, addr);
- if (eval == (cx18_readl(cx, addr) & mask))
+ r = cx18_readl(cx, addr);
+ if (r == 0xffffffff && eval != 0xffffffff)
+ continue;
+ if (eval == (r & mask))
break;
}
}
diff --git a/linux/drivers/media/video/cx18/cx18-ioctl.c b/linux/drivers/media/video/cx18/cx18-ioctl.c
index 5d9c1146e..84d6381aa 100644
--- a/linux/drivers/media/video/cx18/cx18-ioctl.c
+++ b/linux/drivers/media/video/cx18/cx18-ioctl.c
@@ -284,13 +284,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);
}
@@ -315,9 +314,9 @@ static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh,
if (check_service_set(vbifmt, cx->is_50hz) == 0)
return -EINVAL;
- if (atomic_read(&cx->ana_capturing) > 0 &&
- cx->vbi.sliced_in->service_set == 0)
+ if (cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)
return -EBUSY;
+ cx->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
memcpy(cx->vbi.sliced_in, vbifmt, sizeof(*cx->vbi.sliced_in));
return 0;
@@ -327,30 +326,24 @@ static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh,
}
static int cx18_g_chip_ident(struct file *file, void *fh,
- struct v4l2_chip_ident *chip)
+ struct v4l2_dbg_chip_ident *chip)
{
struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
chip->ident = V4L2_IDENT_NONE;
chip->revision = 0;
- if (chip->match_type == V4L2_CHIP_MATCH_HOST) {
- if (v4l2_chip_match_host(chip->match_type, chip->match_chip))
- chip->ident = V4L2_IDENT_CX23418;
+ if (v4l2_chip_match_host(&chip->match)) {
+ chip->ident = V4L2_IDENT_CX23418;
return 0;
}
- if (chip->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
- return cx18_i2c_id(cx, chip->match_chip, VIDIOC_G_CHIP_IDENT,
- chip);
- if (chip->match_type == V4L2_CHIP_MATCH_I2C_ADDR)
- return cx18_call_i2c_client(cx, chip->match_chip,
- VIDIOC_G_CHIP_IDENT, chip);
- return -EINVAL;
+ cx18_call_i2c_clients(cx, VIDIOC_DBG_G_CHIP_IDENT, chip);
+ return 0;
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg)
{
- struct v4l2_register *regs = arg;
+ struct v4l2_dbg_register *regs = arg;
unsigned long flags;
if (!capable(CAP_SYS_ADMIN))
@@ -359,6 +352,7 @@ static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg)
return -EINVAL;
spin_lock_irqsave(&cx18_cards_lock, flags);
+ regs->size = 4;
if (cmd == VIDIOC_DBG_G_REGISTER)
regs->val = cx18_read_enc(cx, regs->reg);
else
@@ -368,31 +362,25 @@ static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg)
}
static int cx18_g_register(struct file *file, void *fh,
- struct v4l2_register *reg)
+ struct v4l2_dbg_register *reg)
{
struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
- if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ if (v4l2_chip_match_host(&reg->match))
return cx18_cxc(cx, VIDIOC_DBG_G_REGISTER, reg);
- if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
- return cx18_i2c_id(cx, reg->match_chip, VIDIOC_DBG_G_REGISTER,
- reg);
- return cx18_call_i2c_client(cx, reg->match_chip, VIDIOC_DBG_G_REGISTER,
- reg);
+ cx18_call_i2c_clients(cx, VIDIOC_DBG_G_REGISTER, reg);
+ return 0;
}
static int cx18_s_register(struct file *file, void *fh,
- struct v4l2_register *reg)
+ struct v4l2_dbg_register *reg)
{
struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
- if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ if (v4l2_chip_match_host(&reg->match))
return cx18_cxc(cx, VIDIOC_DBG_S_REGISTER, reg);
- if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
- return cx18_i2c_id(cx, reg->match_chip, VIDIOC_DBG_S_REGISTER,
- reg);
- return cx18_call_i2c_client(cx, reg->match_chip, VIDIOC_DBG_S_REGISTER,
- reg);
+ cx18_call_i2c_clients(cx, VIDIOC_DBG_S_REGISTER, reg);
+ return 0;
}
#endif
@@ -851,8 +839,7 @@ 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",
@@ -862,7 +849,7 @@ static int cx18_log_status(struct file *file, void *fh)
return 0;
}
-static int cx18_default(struct file *file, void *fh, int cmd, void *arg)
+static long cx18_default(struct file *file, void *fh, int cmd, void *arg)
{
struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
@@ -890,19 +877,19 @@ static int cx18_default(struct file *file, void *fh, int cmd, void *arg)
return 0;
}
-int cx18_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+long cx18_v4l2_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
struct video_device *vfd = video_devdata(filp);
struct cx18_open_id *id = (struct cx18_open_id *)filp->private_data;
struct cx18 *cx = id->cx;
- int res;
+ long res;
mutex_lock(&cx->serialize_lock);
if (cx18_debug & CX18_DBGFLG_IOCTL)
vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
- res = video_ioctl2(inode, filp, cmd, arg);
+ res = video_ioctl2(filp, cmd, arg);
vfd->debug = 0;
mutex_unlock(&cx->serialize_lock);
return res;
diff --git a/linux/drivers/media/video/cx18/cx18-ioctl.h b/linux/drivers/media/video/cx18/cx18-ioctl.h
index 08fe24e95..e2ca0d152 100644
--- a/linux/drivers/media/video/cx18/cx18-ioctl.h
+++ b/linux/drivers/media/video/cx18/cx18-ioctl.h
@@ -29,5 +29,5 @@ void cx18_set_funcs(struct video_device *vdev);
int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std);
int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf);
int cx18_s_input(struct file *file, void *fh, unsigned int inp);
-int cx18_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+long cx18_v4l2_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg);
diff --git a/linux/drivers/media/video/cx18/cx18-mailbox.c b/linux/drivers/media/video/cx18/cx18-mailbox.c
index 8415b9683..89a45f51f 100644
--- a/linux/drivers/media/video/cx18/cx18-mailbox.c
+++ b/linux/drivers/media/video/cx18/cx18-mailbox.c
@@ -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),
};
@@ -163,7 +164,7 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_epu_work_order *order)
* it's filled in).
*
* cx18_queue_get buf() will detect the lost buffers
- * and put them back in rotation eventually.
+ * 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 &&
@@ -174,33 +175,27 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_epu_work_order *order)
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;
}
- 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);
-
dvb_dmx_swfilter(&s->dvb.demux, buf->buf,
buf->bytesused);
-
- cx18_buf_sync_for_device(s, buf);
- cx18_enqueue(s, buf, &s->q_free);
-
- if (s->handle != CX18_INVALID_TASK_HANDLE &&
- test_bit(CX18_F_S_STREAMING, &s->s_flags))
- 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
- set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags);
+ }
+ /* 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)
@@ -460,7 +455,7 @@ void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu)
*/
submit = epu_cmd_irq(cx, order);
if (submit > 0) {
- queue_work(cx18_work_queue, &order->work);
+ queue_work(cx->work_queue, &order->work);
}
}
@@ -618,8 +613,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/linux/drivers/media/video/cx18/cx18-mailbox.h b/linux/drivers/media/video/cx18/cx18-mailbox.h
index 35104458e..a667f1ae4 100644
--- a/linux/drivers/media/video/cx18/cx18-mailbox.h
+++ b/linux/drivers/media/video/cx18/cx18-mailbox.h
@@ -79,6 +79,13 @@ 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, ...);
diff --git a/linux/drivers/media/video/cx18/cx18-queue.c b/linux/drivers/media/video/cx18/cx18-queue.c
index 7b09c9a3e..8d9441e88 100644
--- a/linux/drivers/media/video/cx18/cx18-queue.c
+++ b/linux/drivers/media/video/cx18/cx18-queue.c
@@ -42,21 +42,33 @@ 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)
{
- /* 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;
}
+
mutex_lock(&s->qlock);
- list_add_tail(&buf->list, &q->list);
- atomic_inc(&q->buffers);
+
+ /* 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;
+ atomic_inc(&q->buffers);
+
mutex_unlock(&s->qlock);
+ return q;
}
struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q)
@@ -65,11 +77,11 @@ struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q)
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);
}
mutex_unlock(&s->qlock);
return buf;
@@ -80,61 +92,51 @@ struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id,
{
struct cx18 *cx = s->cx;
struct cx18_buffer *buf;
+ struct cx18_buffer *tmp;
struct cx18_buffer *ret = NULL;
- struct list_head *p, *t;
- LIST_HEAD(r);
mutex_lock(&s->qlock);
- list_for_each_safe(p, t, &s->q_free.list) {
- buf = list_entry(p, struct cx18_buffer, list);
-
+ 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_free.buffers)-1) {
+ if (buf->skipped >= atomic_read(&s->q_busy.buffers)-1) {
/* buffer must have fallen out of rotation */
- atomic_dec(&s->q_free.buffers);
- list_move_tail(&buf->list, &r);
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);
+ /* 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, but for sweeping up lost
- * buffers, we want the TS to requeue the buffer just
- * before sending the MDL back to the firmware, so we
- * pull it off the list here.
+ * 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 {
- atomic_inc(&s->q_full.buffers);
- s->q_full.bytesused += buf->bytesused;
+ /* 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);
ret = buf;
break;
}
mutex_unlock(&s->qlock);
-
- /* Put lost buffers back into firmware transfer rotation */
- while (!list_empty(&r)) {
- buf = list_entry(r.next, struct cx18_buffer, list);
- list_del_init(r.next);
- 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);
- CX18_INFO("Returning %s, buffer %d back to transfer rotation\n",
- s->name, buf->id);
- /* and there was much rejoicing... */
- }
return ret;
}
@@ -148,8 +150,8 @@ static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q)
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 = 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);
}
@@ -159,7 +161,7 @@ static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q)
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/linux/drivers/media/video/cx18/cx18-queue.h b/linux/drivers/media/video/cx18/cx18-queue.h
index ff50a2b7e..456cec3bc 100644
--- a/linux/drivers/media/video/cx18/cx18-queue.h
+++ b/linux/drivers/media/video/cx18/cx18-queue.h
@@ -43,9 +43,24 @@ 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(struct cx18_stream *s, u32 id,
u32 bytesused);
diff --git a/linux/drivers/media/video/cx18/cx18-scb.c b/linux/drivers/media/video/cx18/cx18-scb.c
index ac18bd932..34b4d03c5 100644
--- a/linux/drivers/media/video/cx18/cx18-scb.c
+++ b/linux/drivers/media/video/cx18/cx18-scb.c
@@ -118,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/linux/drivers/media/video/cx18/cx18-streams.c b/linux/drivers/media/video/cx18/cx18-streams.c
index f7a7f38d8..5eb99ea7b 100644
--- a/linux/drivers/media/video/cx18/cx18-streams.c
+++ b/linux/drivers/media/video/cx18/cx18-streams.c
@@ -37,13 +37,12 @@
#define CX18_DSP0_INTERRUPT_MASK 0xd0004C
-static struct file_operations cx18_v4l2_enc_fops = {
+static struct v4l2_file_operations cx18_v4l2_enc_fops = {
.owner = THIS_MODULE,
.read = cx18_v4l2_read,
.open = cx18_v4l2_open,
/* FIXME change to video_ioctl2 if serialization lock can be removed */
.ioctl = cx18_v4l2_ioctl,
- .compat_ioctl = v4l_compat_ioctl32,
.release = cx18_v4l2_close,
.poll = cx18_v4l2_enc_poll,
};
@@ -61,49 +60,41 @@ static struct {
int num_offset;
int dma;
enum v4l2_buf_type buf_type;
- struct file_operations *fops;
} cx18_stream_info[] = {
{ /* CX18_ENC_STREAM_TYPE_MPG */
"encoder MPEG",
VFL_TYPE_GRABBER, 0,
PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
- &cx18_v4l2_enc_fops
},
{ /* CX18_ENC_STREAM_TYPE_TS */
"TS",
VFL_TYPE_GRABBER, -1,
PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
- &cx18_v4l2_enc_fops
},
{ /* CX18_ENC_STREAM_TYPE_YUV */
"encoder YUV",
VFL_TYPE_GRABBER, CX18_V4L2_ENC_YUV_OFFSET,
PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
- &cx18_v4l2_enc_fops
},
{ /* CX18_ENC_STREAM_TYPE_VBI */
"encoder VBI",
VFL_TYPE_VBI, 0,
PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VBI_CAPTURE,
- &cx18_v4l2_enc_fops
},
{ /* CX18_ENC_STREAM_TYPE_PCM */
"encoder PCM audio",
VFL_TYPE_GRABBER, CX18_V4L2_ENC_PCM_OFFSET,
PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_PRIVATE,
- &cx18_v4l2_enc_fops
},
{ /* CX18_ENC_STREAM_TYPE_IDX */
"encoder IDX",
VFL_TYPE_GRABBER, -1,
PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
- &cx18_v4l2_enc_fops
},
{ /* CX18_ENC_STREAM_TYPE_RAD */
"encoder radio",
VFL_TYPE_RADIO, 0,
PCI_DMA_NONE, V4L2_BUF_TYPE_PRIVATE,
- &cx18_v4l2_enc_fops
},
};
@@ -111,7 +102,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));
@@ -124,21 +114,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;
- }
+
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)
@@ -168,7 +152,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;
}
@@ -191,7 +175,7 @@ static int cx18_prep_dev(struct cx18 *cx, int type)
s->v4l2dev->num = num;
s->v4l2dev->parent = &cx->dev->dev;
- s->v4l2dev->fops = cx18_stream_info[type].fops;
+ s->v4l2dev->fops = &cx18_v4l2_enc_fops;
s->v4l2dev->release = video_device_release;
s->v4l2dev->tvnorms = V4L2_STD_ALL;
cx18_set_funcs(s->v4l2dev);
@@ -268,8 +252,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:
@@ -278,10 +263,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);
@@ -345,7 +331,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;
@@ -363,8 +349,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) {
@@ -382,14 +367,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;
@@ -402,11 +388,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;
@@ -435,8 +462,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,
@@ -458,6 +485,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);
@@ -477,7 +506,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) {
@@ -489,16 +520,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");
@@ -507,9 +539,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;
}
diff --git a/linux/drivers/media/video/cx18/cx18-streams.h b/linux/drivers/media/video/cx18/cx18-streams.h
index 7218b1504..420e0a172 100644
--- a/linux/drivers/media/video/cx18/cx18-streams.h
+++ b/linux/drivers/media/video/cx18/cx18-streams.h
@@ -29,6 +29,9 @@ 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/linux/drivers/media/video/cx18/cx18-vbi.c b/linux/drivers/media/video/cx18/cx18-vbi.c
index 22e76ee3f..fb595bd54 100644
--- a/linux/drivers/media/video/cx18/cx18-vbi.c
+++ b/linux/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/linux/drivers/media/video/cx18/cx18-version.h b/linux/drivers/media/video/cx18/cx18-version.h
index eb043d599..84c0ff13b 100644
--- a/linux/drivers/media/video/cx18/cx18-version.h
+++ b/linux/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 3
+#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/linux/drivers/media/video/cx18/cx23418.h b/linux/drivers/media/video/cx18/cx23418.h
index 668f968d7..601f3a2ab 100644
--- a/linux/drivers/media/video/cx18/cx23418.h
+++ b/linux/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_... */