From 8b21c1e90e633aee3363e3d87e1a0b829bdb420e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 14 Dec 2008 19:39:03 -0300 Subject: V4L/DVB (9885): drivers/media Kconfig's: fix bugzilla #12204 When the tuner modules were moved to common/tuners, a separate customize option were added for tuners. However, the automatic selection of the tuners were still using the older option. This causes that the automatic selection to fail, if DVB_FE_CUSTOMISE is selected. Also, since those tuners are now under MEDIA_TUNER_CUSTOMIZE menu, if you unset MEDIA_TUNER_CUSTOMIZE, you can't manually select the tuners. This patch fixes this error by replacing DVB_FE_CUSTOMISE by MEDIA_TUNER_CUSTOMIZE on all places were a tuner is selected. The patch were generated by this small script: for i in `find drivers/media -name Kconfig`; do cat $i|perl -ne 's/(MEDIA_TUNER.*)DVB_FE_CUSTOMISE/\1MEDIA_TUNER_CUSTOMIZE/; print $_' >a mv a $i done Also, manually reordered the tuner entries. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/Kconfig b/drivers/media/video/cx18/Kconfig index ef48565de7f1..8940b5387dec 100644 --- a/drivers/media/video/cx18/Kconfig +++ b/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. -- cgit v1.2.3 From 667d75e7c61e73a59ff452b3a5ce04d9b3e8eca5 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sat, 25 Oct 2008 21:33:22 -0300 Subject: V4L/DVB (9474): cx18: Remove redundant block scope variable in cx18_probe() for sparse cx18: Remove redundant block scope variable in cx18_probe() to eliminate sparse build warning. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-driver.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 7874d9790a51..d7879fb1d21f 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -739,8 +739,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; -- cgit v1.2.3 From ced07371d931884c9c89b66bfe951ea0148a8d08 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 2 Nov 2008 10:59:04 -0300 Subject: V4L/DVB (9512): cx18: Fix write retries for registers that always change - part 3. cx18: Fix write retries for registers that always change - part 3. Fix the io for the rest of the registers that will often not read back the value just written. Modified register readback checks to make sure the intended effect was achieved without constantly rewriting the registers. The one outstanding register remaining is 0xc72014 CX18_AUDIO_ENABLE, whose behavior on writes I have yet to determine. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-av-audio.c | 41 +++++++++---- drivers/media/video/cx18/cx18-av-core.c | 95 +++++++++++++++++++++-------- drivers/media/video/cx18/cx18-av-core.h | 3 + drivers/media/video/cx18/cx18-av-firmware.c | 18 +++--- drivers/media/video/cx18/cx18-firmware.c | 60 ++++++++++++------ drivers/media/video/cx18/cx18-gpio.c | 24 +++++--- drivers/media/video/cx18/cx18-i2c.c | 24 +++++--- 7 files changed, 185 insertions(+), 80 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-av-audio.c b/drivers/media/video/cx18/cx18-av-audio.c index 0b55837880a7..486cad0c26e0 100644 --- a/drivers/media/video/cx18/cx18-av-audio.c +++ b/drivers/media/video/cx18/cx18-av-audio.c @@ -215,12 +215,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 +243,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 +352,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 +384,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 73f5141a42d1..5c079e35e611 100644 --- a/drivers/media/video/cx18/cx18-av-core.c +++ b/drivers/media/video/cx18/cx18-av-core.c @@ -36,12 +36,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); @@ -98,14 +117,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 +146,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); @@ -324,6 +346,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 +356,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 +394,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 +440,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 +833,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 b67d8df20cc6..a07988c6f5cd 100644 --- a/drivers/media/video/cx18/cx18-av-core.h +++ b/drivers/media/video/cx18/cx18-av-core.h @@ -302,6 +302,9 @@ 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); diff --git a/drivers/media/video/cx18/cx18-av-firmware.c b/drivers/media/video/cx18/cx18-av-firmware.c index 522a035b2e8f..caa748a1add1 100644 --- a/drivers/media/video/cx18/cx18-av-firmware.c +++ b/drivers/media/video/cx18/cx18-av-firmware.c @@ -43,11 +43,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); @@ -93,7 +95,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 +121,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 +140,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-firmware.c b/drivers/media/video/cx18/cx18-firmware.c index 51534428cd00..7a7c89032caa 100644 --- a/drivers/media/video/cx18/cx18-firmware.c +++ b/drivers/media/video/cx18/cx18-firmware.c @@ -200,8 +200,10 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx) 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,7 +213,8 @@ 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); + cx18_write_reg_expect(cx, 0x00020000, CX18_ADEC_CONTROL, + 0x00000000, 0x00020002); /* The fast clock is at 200/245 MHz */ cx18_write_reg(cx, lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT); @@ -248,22 +251,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 +297,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); @@ -310,7 +327,9 @@ int cx18_firmware_init(struct cx18 *cx) /* 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); @@ -325,7 +344,8 @@ int cx18_firmware_init(struct cx18 *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_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", @@ -335,7 +355,9 @@ int cx18_firmware_init(struct cx18 *cx) int retries = 0; /* start the CPU */ - cx18_write_reg(cx, 0x00080000, CX18_PROC_SOFT_RESET); + 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) @@ -352,6 +374,6 @@ int cx18_firmware_init(struct cx18 *cx) return -EIO; } /* 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 0e560421989e..17b7a32fcc31 100644 --- a/drivers/media/video/cx18/cx18-gpio.c +++ b/drivers/media/video/cx18/cx18-gpio.c @@ -47,15 +47,21 @@ 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); + if (!cx18_retry_mmio) + (void) cx18_read_reg(cx, CX18_REG_GPIO_OUT2); /* sync */ } void cx18_reset_i2c_slaves_gpio(struct cx18 *cx) diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c index aa09e557b195..824efbecb34c 100644 --- a/drivers/media/video/cx18/cx18-i2c.c +++ b/drivers/media/video/cx18/cx18-i2c.c @@ -27,6 +27,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 @@ -396,22 +397,31 @@ 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 */ - cx18_write_reg_sync(cx, 0x00c00000, 0xc7001c); + cx18_write_reg_expect(cx, 0x00c00000, 0xc7001c, 0x00000000, 0x00c000c0); + if (!cx18_retry_mmio) + (void) cx18_read_reg(cx, 0xc7001c); /* sync */ mdelay(10); - cx18_write_reg_sync(cx, 0x00c000c0, 0xc7001c); + cx18_write_reg_expect(cx, 0x00c000c0, 0xc7001c, 0x000000c0, 0x00c000c0); + if (!cx18_retry_mmio) + (void) cx18_read_reg(cx, 0xc7001c); /* sync */ mdelay(10); - cx18_write_reg_sync(cx, 0x00c00000, 0xc7001c); + cx18_write_reg_expect(cx, 0x00c00000, 0xc7001c, 0x00000000, 0x00c000c0); + if (!cx18_retry_mmio) + (void) cx18_read_reg(cx, 0xc7001c); /* sync */ 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); -- cgit v1.2.3 From f4167342b572c6122c712e8c6e5391808c1e5503 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 2 Nov 2008 18:54:10 -0300 Subject: V4L/DVB (9514): cx18: Fix PLL freq computation for debug display cx18: Fix PLL freq computation for debug display. The code to compute the PLL freq from register values was storing an intermediate 56 bit result in a 32 bit type, causing a nonsense value to be displayed. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-av-core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c index 5c079e35e611..518bd701d393 100644 --- a/drivers/media/video/cx18/cx18-av-core.c +++ b/drivers/media/video/cx18/cx18-av-core.c @@ -273,10 +273,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 = (28636364L * ((((u64)pll_int) << 25) + pll_frac)) >> 25; pll /= pll_post; CX18_DEBUG_INFO("PLL = %d.%06d MHz\n", pll / 1000000, pll % 1000000); -- cgit v1.2.3 From f7823f8f437fbbd41155f2312ef17e471199b706 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 2 Nov 2008 18:15:28 -0300 Subject: V4L/DVB (9513): cx18: Reduce number of mmio read retries cx18: Reduce number of mmio read retries to improve performance. Experiments have shown 2 things: read retries never improve the result of a suspect mmio read from the CX23418 (the result stays all 0xff's), and that most of the suspected read failures are actually proper reads of values that should be all 0xff's. This change reduces the number of read retries and keeps the count separate from write retries. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-av-firmware.c | 4 ++-- drivers/media/video/cx18/cx18-driver.h | 7 ++++--- drivers/media/video/cx18/cx18-io.c | 24 ++++++++++++------------ drivers/media/video/cx18/cx18-io.h | 8 ++++---- 4 files changed, 22 insertions(+), 21 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-av-firmware.c b/drivers/media/video/cx18/cx18-av-firmware.c index caa748a1add1..924691dcaeb7 100644 --- a/drivers/media/video/cx18/cx18-av-firmware.c +++ b/drivers/media/video/cx18/cx18-av-firmware.c @@ -63,7 +63,7 @@ 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); @@ -82,7 +82,7 @@ int cx18_av_loadfw(struct cx18 *cx) } 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) diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index bbdd5f25041d..fe9e5bb979e0 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -353,11 +353,12 @@ 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 +#define CX18_MAX_MMIO_WR_RETRIES 10 +#define CX18_MAX_MMIO_RD_RETRIES 2 struct cx18_mmio_stats { - atomic_t retried_write[CX18_MAX_MMIO_RETRIES+1]; - atomic_t retried_read[CX18_MAX_MMIO_RETRIES+1]; + atomic_t retried_write[CX18_MAX_MMIO_WR_RETRIES+1]; + atomic_t retried_read[CX18_MAX_MMIO_RD_RETRIES+1]; }; /* Struct to hold info about cx18 cards */ diff --git a/drivers/media/video/cx18/cx18-io.c b/drivers/media/video/cx18/cx18-io.c index 220fae8d4ad7..0ad8dea3e600 100644 --- a/drivers/media/video/cx18/cx18-io.c +++ b/drivers/media/video/cx18/cx18-io.c @@ -31,10 +31,10 @@ void cx18_log_statistics(struct cx18 *cx) if (!(cx18_debug & CX18_DBGFLG_INFO)) return; - for (i = 0; i <= CX18_MAX_MMIO_RETRIES; i++) + for (i = 0; i <= CX18_MAX_MMIO_WR_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++) + for (i = 0; i <= CX18_MAX_MMIO_RD_RETRIES; i++) CX18_DEBUG_INFO("retried_read[%d] = %d\n", i, atomic_read(&cx->mmio_stats.retried_read[i])); return; @@ -43,7 +43,7 @@ void cx18_log_statistics(struct cx18 *cx) void cx18_raw_writel_retry(struct cx18 *cx, u32 val, void __iomem *addr) { int i; - for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) { + for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) { cx18_raw_writel_noretry(cx, val, addr); if (val == cx18_raw_readl_noretry(cx, addr)) break; @@ -55,7 +55,7 @@ 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++) { + for (i = 0; i < CX18_MAX_MMIO_RD_RETRIES; i++) { val = cx18_raw_readl_noretry(cx, addr); if (val != 0xffffffff) /* PCI bus read error */ break; @@ -68,7 +68,7 @@ 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++) { + for (i = 0; i < CX18_MAX_MMIO_RD_RETRIES; i++) { val = cx18_raw_readw_noretry(cx, addr); if (val != 0xffff) /* PCI bus read error */ break; @@ -80,7 +80,7 @@ u16 cx18_raw_readw_retry(struct cx18 *cx, const void __iomem *addr) void cx18_writel_retry(struct cx18 *cx, u32 val, void __iomem *addr) { int i; - for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) { + for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) { cx18_writel_noretry(cx, val, addr); if (val == cx18_readl_noretry(cx, addr)) break; @@ -93,7 +93,7 @@ void _cx18_writel_expect(struct cx18 *cx, u32 val, void __iomem *addr, { int i; eval &= mask; - for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) { + for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) { cx18_writel_noretry(cx, val, addr); if (eval == (cx18_readl_noretry(cx, addr) & mask)) break; @@ -104,7 +104,7 @@ void _cx18_writel_expect(struct cx18 *cx, u32 val, void __iomem *addr, void cx18_writew_retry(struct cx18 *cx, u16 val, void __iomem *addr) { int i; - for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) { + for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) { cx18_writew_noretry(cx, val, addr); if (val == cx18_readw_noretry(cx, addr)) break; @@ -115,7 +115,7 @@ void cx18_writew_retry(struct cx18 *cx, u16 val, void __iomem *addr) void cx18_writeb_retry(struct cx18 *cx, u8 val, void __iomem *addr) { int i; - for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) { + for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) { cx18_writeb_noretry(cx, val, addr); if (val == cx18_readb_noretry(cx, addr)) break; @@ -127,7 +127,7 @@ u32 cx18_readl_retry(struct cx18 *cx, const void __iomem *addr) { int i; u32 val; - for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) { + for (i = 0; i < CX18_MAX_MMIO_RD_RETRIES; i++) { val = cx18_readl_noretry(cx, addr); if (val != 0xffffffff) /* PCI bus read error */ break; @@ -140,7 +140,7 @@ u16 cx18_readw_retry(struct cx18 *cx, const void __iomem *addr) { int i; u16 val; - for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) { + for (i = 0; i < CX18_MAX_MMIO_RD_RETRIES; i++) { val = cx18_readw_noretry(cx, addr); if (val != 0xffff) /* PCI bus read error */ break; @@ -153,7 +153,7 @@ u8 cx18_readb_retry(struct cx18 *cx, const void __iomem *addr) { int i; u8 val; - for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) { + for (i = 0; i < CX18_MAX_MMIO_RD_RETRIES; i++) { val = cx18_readb_noretry(cx, addr); if (val != 0xff) /* PCI bus read error */ break; diff --git a/drivers/media/video/cx18/cx18-io.h b/drivers/media/video/cx18/cx18-io.h index 425244453ea7..cb695a59670d 100644 --- a/drivers/media/video/cx18/cx18-io.h +++ b/drivers/media/video/cx18/cx18-io.h @@ -41,8 +41,8 @@ static inline void cx18_io_delay(struct cx18 *cx) 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; + if (i > CX18_MAX_MMIO_WR_RETRIES) + i = CX18_MAX_MMIO_WR_RETRIES; atomic_inc(&cx->mmio_stats.retried_write[i]); return; } @@ -50,8 +50,8 @@ void cx18_log_write_retries(struct cx18 *cx, int i, const void __iomem *addr) static inline void cx18_log_read_retries(struct cx18 *cx, int i, const void __iomem *addr) { - if (i > CX18_MAX_MMIO_RETRIES) - i = CX18_MAX_MMIO_RETRIES; + if (i > CX18_MAX_MMIO_RD_RETRIES) + i = CX18_MAX_MMIO_RD_RETRIES; atomic_inc(&cx->mmio_stats.retried_read[i]); return; } -- cgit v1.2.3 From f68d0cf56761128e85ebc98d8de4776b89c30279 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Wed, 5 Nov 2008 21:19:15 -0300 Subject: V4L/DVB (9592): cx18: Use default kernel work queue; fix streaming flag for work handler cx18: Use default kernel work queue; fix streaming flag for work handler. Eliminate cx18 specific work queue and use the kernel default work queue. Fixed the F_STREAMING_FLAG for the TS stream so cx18_dvb_work_handler() can know when it is not safe to send MDLs to the firmware. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-driver.c | 10 +-------- drivers/media/video/cx18/cx18-driver.h | 2 -- drivers/media/video/cx18/cx18-dvb.c | 37 +++++++++++++++++++-------------- drivers/media/video/cx18/cx18-irq.c | 8 +------ drivers/media/video/cx18/cx18-streams.c | 6 +++--- 5 files changed, 26 insertions(+), 37 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index d7879fb1d21f..6799eab8b6ac 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -449,12 +449,6 @@ 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("Could not create work queue\n"); - return -1; - } - INIT_WORK(&cx->work, cx18_work_handler); /* start counting open_id at 1 */ @@ -837,7 +831,6 @@ 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; @@ -938,8 +931,7 @@ static void cx18_remove(struct pci_dev *pci_dev) cx18_halt_firmware(cx); - flush_workqueue(cx->work_queue); - destroy_workqueue(cx->work_queue); + flush_scheduled_work(); cx18_streams_cleanup(cx, 1); diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index fe9e5bb979e0..04f1c624da7c 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -207,7 +207,6 @@ struct cx18_options { #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) @@ -435,7 +434,6 @@ struct cx18 { /* when the current DMA is finished this queue is woken up */ wait_queue_head_t dma_waitq; - struct workqueue_struct *work_queue; struct work_struct work; /* i2c */ diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c index 4542e2e5e3d7..4845f732ef48 100644 --- a/drivers/media/video/cx18/cx18-dvb.c +++ b/drivers/media/video/cx18/cx18-dvb.c @@ -109,20 +109,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; } @@ -313,9 +316,11 @@ void cx18_dvb_work_handler(struct cx18 *cx) 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 */ + cx18_enqueue(s, buf, &s->q_free); + + if (s->handle == CX18_INVALID_TASK_HANDLE || + !test_bit(CX18_F_S_STREAMING, &s->s_flags)) continue; cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, diff --git a/drivers/media/video/cx18/cx18-irq.c b/drivers/media/video/cx18/cx18-irq.c index 5fbfbd0f1493..33f56c61ca74 100644 --- a/drivers/media/video/cx18/cx18-irq.c +++ b/drivers/media/video/cx18/cx18-irq.c @@ -34,12 +34,6 @@ 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, ¶m); - } if (test_and_clear_bit(CX18_F_I_WORK_HANDLER_DVB, &cx->i_flags)) cx18_dvb_work_handler(cx); } @@ -194,7 +188,7 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id) epu_cmd(cx, sw1); if (test_and_clear_bit(CX18_F_I_HAVE_WORK, &cx->i_flags)) - queue_work(cx->work_queue, &cx->work); + schedule_work(&cx->work); return (sw1 || sw2 || hw2) ? IRQ_HANDLED : IRQ_NONE; } diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index e5ff7705b7a1..c7d431f49238 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -560,9 +560,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 +567,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; -- cgit v1.2.3 From 72c2d6d3ac91d1b9efb482ff4a8dd68e3d867965 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 6 Nov 2008 01:15:41 -0300 Subject: V4L/DVB (9593): cx18: Add outgoing mailbox mutexes and check for ack via waitq vs poll Add mutexes to ensure exclusive access for outgoing driver to CX23418 mailboxes. Also wait on a waitq for mailbox acknowledgement from the CX23418 instead of polling. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-driver.c | 4 +- drivers/media/video/cx18/cx18-driver.h | 7 +-- drivers/media/video/cx18/cx18-irq.c | 21 +++------ drivers/media/video/cx18/cx18-mailbox.c | 84 ++++++++++++++++----------------- drivers/media/video/cx18/cx18-mailbox.h | 7 ++- 5 files changed, 60 insertions(+), 63 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 6799eab8b6ac..3079e466e6e7 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -446,6 +446,8 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) 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); @@ -466,8 +468,6 @@ 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 */ diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index 04f1c624da7c..e2ec15549783 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -378,7 +378,10 @@ 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; @@ -428,8 +431,6 @@ 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; diff --git a/drivers/media/video/cx18/cx18-irq.c b/drivers/media/video/cx18/cx18-irq.c index 33f56c61ca74..37b931055d3a 100644 --- a/drivers/media/video/cx18/cx18-irq.c +++ b/drivers/media/video/cx18/cx18-irq.c @@ -38,7 +38,7 @@ void cx18_work_handler(struct work_struct *work) cx18_dvb_work_handler(cx); } -static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb) +static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb, int rpu) { u32 handle = mb->args[0]; struct cx18_stream *s = NULL; @@ -59,7 +59,7 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb) " handle %d\n", handle); mb->error = CXERR_NOT_OPEN; mb->cmd = 0; - cx18_mb_ack(cx, mb); + cx18_mb_ack(cx, mb, rpu); return; } @@ -86,13 +86,13 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb) } mb->error = 0; mb->cmd = 0; - cx18_mb_ack(cx, mb); + cx18_mb_ack(cx, mb, rpu); wake_up(&cx->dma_waitq); if (s->id != -1) wake_up(&s->waitq); } -static void epu_debug(struct cx18 *cx, struct cx18_mailbox *mb) +static void epu_debug(struct cx18 *cx, struct cx18_mailbox *mb, int rpu) { char str[256] = { 0 }; char *p; @@ -102,7 +102,7 @@ static void epu_debug(struct cx18 *cx, struct cx18_mailbox *mb) cx18_memcpy_fromio(cx, str, cx->enc_mem + mb->args[1], 252); str[252] = 0; } - cx18_mb_ack(cx, mb); + cx18_mb_ack(cx, mb, rpu); 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) @@ -119,10 +119,10 @@ static void epu_cmd(struct cx18 *cx, u32 sw1) switch (mb.cmd) { case CX18_EPU_DMA_DONE: - epu_dma_done(cx, &mb); + epu_dma_done(cx, &mb, CPU); break; case CX18_EPU_DEBUG: - epu_debug(cx, &mb); + epu_debug(cx, &mb, CPU); break; default: CX18_WARN("Unknown CPU_TO_EPU mailbox command %#08x\n", @@ -135,11 +135,6 @@ static void epu_cmd(struct cx18 *cx, u32 sw1) 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) @@ -148,8 +143,6 @@ 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); } irqreturn_t cx18_irq_handler(int irq, void *dev_id) diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index acff7dfb60df..d2975a2e6cb7 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -30,11 +30,6 @@ #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 */ @@ -117,10 +112,7 @@ static struct cx18_mailbox __iomem *cx18_mb_is_complete(struct cx18 *cx, int rpu *irq = cx18_readl(cx, &cx->scb->epu2cpu_irq); break; - case HPU: - mb = &cx->scb->epu2hpu_mb; - *state = cx18_readl(cx, &cx->scb->hpu_state); - *irq = cx18_readl(cx, &cx->scb->epu2hpu_irq); + default: break; } @@ -142,25 +134,12 @@ static struct cx18_mailbox __iomem *cx18_mb_is_complete(struct cx18 *cx, int rpu return NULL; } -long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb) +long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb, int rpu) { - const struct cx18_api_info *info = find_api_info(mb->cmd); struct cx18_mailbox __iomem *ack_mb; u32 ack_irq; - u8 rpu = CPU; - - if (info == NULL && mb->cmd) { - CX18_WARN("Cannot ack unknown command %x\n", mb->cmd); - return -EINVAL; - } - if (info) - rpu = info->rpu; switch (rpu) { - case HPU: - ack_irq = IRQ_EPU_TO_HPU_ACK; - ack_mb = &cx->scb->hpu2epu_mb; - break; case APU: ack_irq = IRQ_EPU_TO_APU_ACK; ack_mb = &cx->scb->apu2epu_mb; @@ -170,7 +149,8 @@ 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); + CX18_WARN("Unhandled RPU (%d) for command %x ack\n", + rpu, mb->cmd); return -EINVAL; } @@ -187,8 +167,8 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) u32 state = 0, irq = 0, req, oldreq, err; struct cx18_mailbox __iomem *mb; wait_queue_head_t *waitq; + struct mutex *mb_lock; int timeout = 100; - int cnt = 0; int sig = 0; int i; @@ -201,10 +181,27 @@ 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); + + switch (info->rpu) { + case APU: + waitq = &cx->mb_apu_waitq; + mb_lock = &cx->epu2apu_mb_lock; + break; + case CPU: + waitq = &cx->mb_cpu_waitq; + mb_lock = &cx->epu2cpu_mb_lock; + break; + default: + CX18_WARN("Unknown RPU (%d) for API call\n", info->rpu); + return -EINVAL; + } + + mutex_lock(mb_lock); cx18_setup_page(cx, SCB_OFFSET); mb = cx18_mb_is_complete(cx, info->rpu, &state, &irq, &req); if (mb == NULL) { + mutex_unlock(mb_lock); CX18_ERR("mb %s busy\n", info->name); return -EBUSY; } @@ -216,34 +213,35 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) cx18_writel(cx, 0, &mb->error); cx18_writel(cx, req, &mb->request); - 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; 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) { + sig = wait_event_interruptible_timeout( + *waitq, + cx18_readl(cx, &mb->ack) == cx18_readl(cx, &mb->request), + msecs_to_jiffies(timeout)); + if (sig == 0) { + /* Timed out */ cx18_writel(cx, oldreq, &mb->request); - CX18_ERR("mb %s failed\n", info->name); + mutex_unlock(mb_lock); + CX18_ERR("sending %s timed out waiting for RPU to respond\n", + info->name); return -EINVAL; + } else if (sig < 0) { + /* Interrupted */ + cx18_writel(cx, oldreq, &mb->request); + mutex_unlock(mb_lock); + CX18_WARN("sending %s interrupted waiting for RPU to respond\n", + info->name); + return -EINTR; } + 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); + if (info->flags & API_SLOW) cx18_msleep_timeout(300, 0); if (err) CX18_DEBUG_API("mailbox error %08x for command %s\n", err, diff --git a/drivers/media/video/cx18/cx18-mailbox.h b/drivers/media/video/cx18/cx18-mailbox.h index d995641536b3..54758f32db1e 100644 --- a/drivers/media/video/cx18/cx18-mailbox.h +++ b/drivers/media/video/cx18/cx18-mailbox.h @@ -30,6 +30,11 @@ #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; /* The cx18_mailbox struct is the mailbox structure which is used for passing @@ -68,6 +73,6 @@ int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], u32 cmd, 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); +long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb, int rpu); #endif -- cgit v1.2.3 From d670b6ff4ef32d3a0804ec26ad53a2a7712cec98 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 6 Nov 2008 01:33:27 -0300 Subject: V4L/DVB (9594): cx18: Roll driver version number due to significant changes Driver interrupt and mailbox handling has change significantly. Time for a roll to v1.0.2. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h index 9f6be2d457fb..366cc14729d7 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 2 #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, \ -- cgit v1.2.3 From ac50441720332f22a9d85ac03151d6acb7bc55d6 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Fri, 7 Nov 2008 23:57:46 -0300 Subject: V4L/DVB (9595): cx18: Improve handling of outgoing mailboxes detected to be busy cx18: Improve handling of outgoing mailboxes detected to be busy. When encountering a busy mailbox, sleep instead of polling, and wait for interrupt or timeout. If the mailbox is still busy, force it free. When sending commands, make sure we never create a situation where we mark the mailbox busy upon sending, and ensure we always have a method to cleanly recover from a busy mailbox. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-irq.c | 3 +- drivers/media/video/cx18/cx18-mailbox.c | 130 +++++++++++++++----------------- 2 files changed, 64 insertions(+), 69 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-irq.c b/drivers/media/video/cx18/cx18-irq.c index 37b931055d3a..4912b3c8e6a5 100644 --- a/drivers/media/video/cx18/cx18-irq.c +++ b/drivers/media/video/cx18/cx18-irq.c @@ -167,7 +167,8 @@ 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); /* To do: interrupt-based I2C handling if (hw2 & (HW2_I2C1_INT|HW2_I2C2_INT)) { diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index d2975a2e6cb7..0de4b9a7bbca 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -92,48 +92,6 @@ 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) -{ - 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; - - case CPU: - mb = &cx->scb->epu2cpu_mb; - *state = cx18_readl(cx, &cx->scb->cpu_state); - *irq = cx18_readl(cx, &cx->scb->epu2cpu_irq); - break; - - default: - break; - } - - 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); - - if (*req == ack) { - (*req)++; - if (*req == 0 || *req == 0xffffffff) - *req = 1; - return mb; - } - return NULL; -} - long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb, int rpu) { struct cx18_mailbox __iomem *ack_mb; @@ -164,12 +122,13 @@ long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb, int rpu) 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; struct mutex *mb_lock; int timeout = 100; - int sig = 0; + long unsigned int j, ret; int i; if (info == NULL) { @@ -186,10 +145,16 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) 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); @@ -198,51 +163,85 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) mutex_lock(mb_lock); cx18_setup_page(cx, SCB_OFFSET); - mb = cx18_mb_is_complete(cx, info->rpu, &state, &irq, &req); - if (mb == NULL) { - mutex_unlock(mb_lock); - CX18_ERR("mb %s busy\n", info->name); - return -EBUSY; - } + /* + * 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); + j = msecs_to_jiffies(timeout); + ret = wait_event_timeout(*waitq, + (ack = cx18_readl(cx, &mb->ack)) == req, + j); + 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 != j) + CX18_DEBUG_API("waited %u usecs for busy mbox to be acked\n", + jiffies_to_usecs(j-ret)); + + /* Build the outgoing mailbox */ + req = ((req & 0xfffffffe) == 0xfffffffe) ? 1 : req + 1; - oldreq = 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 */ + /* Notify the XPU and wait for it to send an Ack back */ if (info->flags & API_FAST) timeout /= 2; + j = msecs_to_jiffies(timeout); + + 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); - sig = wait_event_interruptible_timeout( + ret = wait_event_interruptible_timeout( *waitq, cx18_readl(cx, &mb->ack) == cx18_readl(cx, &mb->request), - msecs_to_jiffies(timeout)); - if (sig == 0) { + j); + if (ret == 0) { /* Timed out */ - cx18_writel(cx, oldreq, &mb->request); mutex_unlock(mb_lock); - CX18_ERR("sending %s timed out waiting for RPU to respond\n", - info->name); + CX18_ERR("sending %s timed out waiting for RPU " + "acknowledgement\n", info->name); return -EINVAL; - } else if (sig < 0) { + } else if (ret < 0) { /* Interrupted */ - cx18_writel(cx, oldreq, &mb->request); mutex_unlock(mb_lock); - CX18_WARN("sending %s interrupted waiting for RPU to respond\n", - info->name); + CX18_WARN("sending %s was interrupted waiting for RPU" + "acknowledgement\n", info->name); return -EINTR; - } + } else if (ret != j) + CX18_DEBUG_HI_API("waited %u usecs for %s to be acked\n", + jiffies_to_usecs(j-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); 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); @@ -251,12 +250,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) -- cgit v1.2.3 From 330c6ec8942765e81f237bd58020da1b161935ce Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sat, 8 Nov 2008 14:19:37 -0300 Subject: V4L/DVB (9596): cx18: Further changes to improve mailbox protocol integrity & performnce All waits for cx18 mailbox API commands are now uninterruptable. Added code to collect mailbox ack statistics. Tweaked timeouts based on collected stats and video vertical frame and field rates. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-driver.c | 2 +- drivers/media/video/cx18/cx18-driver.h | 7 +++++ drivers/media/video/cx18/cx18-io.c | 4 +++ drivers/media/video/cx18/cx18-mailbox.c | 47 +++++++++++++++++++++------------ 4 files changed, 42 insertions(+), 18 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 3079e466e6e7..8ef11d578b8d 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -187,7 +187,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 { diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index e2ec15549783..ce76806759ec 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -360,6 +360,12 @@ struct cx18_mmio_stats { atomic_t retried_read[CX18_MAX_MMIO_RD_RETRIES+1]; }; +#define CX18_MAX_MB_ACK_DELAY 100 + +struct cx18_mbox_stats { + atomic_t mb_ack_delay[CX18_MAX_MB_ACK_DELAY+1]; +}; + /* Struct to hold info about cx18 cards */ struct cx18 { int num; /* board number, -1 during init! */ @@ -452,6 +458,7 @@ struct cx18 { /* Statistics */ struct cx18_mmio_stats mmio_stats; + struct cx18_mbox_stats mbox_stats; /* v4l2 and User settings */ diff --git a/drivers/media/video/cx18/cx18-io.c b/drivers/media/video/cx18/cx18-io.c index 0ad8dea3e600..48a8adc83c2f 100644 --- a/drivers/media/video/cx18/cx18-io.c +++ b/drivers/media/video/cx18/cx18-io.c @@ -37,6 +37,10 @@ void cx18_log_statistics(struct cx18 *cx) for (i = 0; i <= CX18_MAX_MMIO_RD_RETRIES; i++) CX18_DEBUG_INFO("retried_read[%d] = %d\n", i, atomic_read(&cx->mmio_stats.retried_read[i])); + for (i = 0; i <= CX18_MAX_MB_ACK_DELAY; i++) + if (atomic_read(&cx->mbox_stats.mb_ack_delay[i])) + CX18_DEBUG_INFO("mb_ack_delay[%d] = %d\n", i, + atomic_read(&cx->mbox_stats.mb_ack_delay[i])); return; } diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index 0de4b9a7bbca..35f7188d4d3b 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -118,6 +118,12 @@ long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb, int rpu) return 0; } +static void cx18_api_log_ack_delay(struct cx18 *cx, int msecs) +{ + if (msecs > CX18_MAX_MB_ACK_DELAY) + msecs = CX18_MAX_MB_ACK_DELAY; + atomic_inc(&cx->mbox_stats.mb_ack_delay[msecs]); +} static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) { @@ -127,8 +133,7 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) u32 __iomem *xpu_state; wait_queue_head_t *waitq; struct mutex *mb_lock; - int timeout = 100; - long unsigned int j, ret; + long int timeout, ret; int i; if (info == NULL) { @@ -176,18 +181,18 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) */ state = cx18_readl(cx, xpu_state); req = cx18_readl(cx, &mb->request); - j = msecs_to_jiffies(timeout); + timeout = msecs_to_jiffies(20); /* 1 field at 50 Hz vertical refresh */ ret = wait_event_timeout(*waitq, (ack = cx18_readl(cx, &mb->ack)) == req, - j); + 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 != j) + } else if (ret != timeout) CX18_DEBUG_API("waited %u usecs for busy mbox to be acked\n", - jiffies_to_usecs(j-ret)); + jiffies_to_usecs(timeout-ret)); /* Build the outgoing mailbox */ req = ((req & 0xfffffffe) == 0xfffffffe) ? 1 : req + 1; @@ -199,24 +204,28 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) cx18_writel(cx, req, &mb->request); cx18_writel(cx, req - 1, &mb->ack); /* ensure ack & req are distinct */ - /* Notify the XPU and wait for it to send an Ack back */ - if (info->flags & API_FAST) - timeout /= 2; - j = msecs_to_jiffies(timeout); + /* + * Notify the XPU and wait for it to send an Ack back + * 21 ms = ~ 0.5 frames at a frame rate of 24 fps + * 42 ms = ~ 1 frame at a frame rate of 24 fps + */ + timeout = msecs_to_jiffies((info->flags & API_FAST) ? 21 : 42); 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); - ret = wait_event_interruptible_timeout( + ret = wait_event_timeout( *waitq, cx18_readl(cx, &mb->ack) == cx18_readl(cx, &mb->request), - j); + timeout); if (ret == 0) { /* Timed out */ mutex_unlock(mb_lock); - CX18_ERR("sending %s timed out waiting for RPU " - "acknowledgement\n", info->name); + i = jiffies_to_msecs(timeout); + cx18_api_log_ack_delay(cx, i); + CX18_WARN("sending %s timed out waiting %d msecs for RPU " + "acknowledgement\n", info->name, i); return -EINVAL; } else if (ret < 0) { /* Interrupted */ @@ -224,9 +233,13 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) CX18_WARN("sending %s was interrupted waiting for RPU" "acknowledgement\n", info->name); return -EINTR; - } else if (ret != j) - CX18_DEBUG_HI_API("waited %u usecs for %s to be acked\n", - jiffies_to_usecs(j-ret), info->name); + } + + i = jiffies_to_msecs(timeout-ret); + cx18_api_log_ack_delay(cx, i); + if (ret != timeout) + CX18_DEBUG_HI_API("waited %u msecs for %s to be acked\n", + i, info->name); /* Collect data returned by the XPU */ for (i = 0; i < MAX_MB_ARGUMENTS; i++) -- cgit v1.2.3 From 2d1a1b055be8598dbcc8a7b905d07bcf05eaff3a Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sat, 8 Nov 2008 17:14:22 -0300 Subject: V4L/DVB (9597): cx18: Minor fixes to APU firmware load process Use the APU fw start address from rom file instead of a hardcoded entry vector. Fixed cx18_setup_page() calls to use the correct APU image load addresses. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-firmware.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-firmware.c b/drivers/media/video/cx18/cx18-firmware.c index 7a7c89032caa..ab02da727519 100644 --- a/drivers/media/video/cx18/cx18-firmware.c +++ b/drivers/media/video/cx18/cx18-firmware.c @@ -134,7 +134,8 @@ static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx) 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; @@ -152,6 +153,7 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx) return -ENOMEM; } + *entry_addr = 0xffffffff; src = (const u32 *)fw->data; vers = fw->data + sizeof(seghdr); sz = fw->size; @@ -168,10 +170,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 == 0xffffffff) + *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], @@ -192,8 +196,6 @@ 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); return size; } @@ -338,11 +340,16 @@ int cx18_firmware_init(struct cx18 *cx) /* Only if the processor is not running */ if (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) { + u32 fw_entry_addr; int sz = load_apu_fw_direct("v4l-cx23418-apu.fw", - cx->enc_mem, cx); + cx->enc_mem, cx, &fw_entry_addr); + + /* 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); - cx18_write_enc(cx, 0xE51FF004, 0); - cx18_write_enc(cx, 0xa00000, 4); /* todo: not hardcoded */ /* Start APU */ cx18_write_reg_expect(cx, 0x00010000, CX18_PROC_SOFT_RESET, 0x00000000, 0x00010001); -- cgit v1.2.3 From d20ceecd0c5370cfe6b6eee2f63fecb65222c747 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 9 Nov 2008 18:14:07 -0300 Subject: V4L/DVB (9598): cx18: Prevent CX23418 from clearing it's outgoing ack interrupts to driver When the CX23418 CPU unit sent out an ack interrupt to the linux driver, it also received that interrupt and cleared the flag before the linux driver could see what the interrupt was for. This fix prevents the CPU from receiving an IRQ for it's own outgoing ack's to the linux driver. This fix is critical now that the linux driver doesn't poll but relies on these ack interrupts. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-firmware.c | 11 +++++++++++ drivers/media/video/cx18/cx18-io.c | 7 +++++++ drivers/media/video/cx18/cx18-io.h | 1 + drivers/media/video/cx18/cx18-irq.h | 1 + 4 files changed, 20 insertions(+) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-firmware.c b/drivers/media/video/cx18/cx18-firmware.c index ab02da727519..06f5563d6d5a 100644 --- a/drivers/media/video/cx18/cx18-firmware.c +++ b/drivers/media/video/cx18/cx18-firmware.c @@ -380,6 +380,17 @@ int cx18_firmware_init(struct cx18 *cx) if (sz <= 0) 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. + */ + cx18_sw2_irq_disable_cpu(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); + /* initialize GPIO */ cx18_write_reg_expect(cx, 0x14001400, 0xc78110, 0x00001400, 0x14001400); return 0; diff --git a/drivers/media/video/cx18/cx18-io.c b/drivers/media/video/cx18/cx18-io.c index 48a8adc83c2f..3c6485fceea0 100644 --- a/drivers/media/video/cx18/cx18-io.c +++ b/drivers/media/video/cx18/cx18-io.c @@ -262,6 +262,13 @@ void cx18_sw2_irq_disable(struct cx18 *cx, u32 val) cx18_write_reg(cx, r & ~val, 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_CPU); + cx18_write_reg(cx, r & ~val, SW2_INT_ENABLE_CPU); +} + void cx18_setup_page(struct cx18 *cx, u32 addr) { u32 val; diff --git a/drivers/media/video/cx18/cx18-io.h b/drivers/media/video/cx18/cx18-io.h index cb695a59670d..4486b73faf5b 100644 --- a/drivers/media/video/cx18/cx18-io.h +++ b/drivers/media/video/cx18/cx18-io.h @@ -390,6 +390,7 @@ 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-irq.h b/drivers/media/video/cx18/cx18-irq.h index 6173ca3bc9e4..6f3ec8963762 100644 --- a/drivers/media/video/cx18/cx18-irq.h +++ b/drivers/media/video/cx18/cx18-irq.h @@ -28,6 +28,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); -- cgit v1.2.3 From c7abfb47c9e19d63ce6f757afcb392c69ce09765 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 9 Nov 2008 19:51:44 -0300 Subject: V4L/DVB (9599): cx18: Fix unitialized variable problem upon APU firmware file read failure If APU firmware file read failed, the jump vector to the APU was undefined and the APU would be started executing garbage. Fix uninitialized variable to be an infinite loop for the APU, but also bail out before even starting the APU. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-firmware.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-firmware.c b/drivers/media/video/cx18/cx18-firmware.c index 06f5563d6d5a..d9c5f55ab17c 100644 --- a/drivers/media/video/cx18/cx18-firmware.c +++ b/drivers/media/video/cx18/cx18-firmware.c @@ -153,7 +153,7 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx, return -ENOMEM; } - *entry_addr = 0xffffffff; + *entry_addr = 0; src = (const u32 *)fw->data; vers = fw->data + sizeof(seghdr); sz = fw->size; @@ -170,7 +170,7 @@ 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 == 0xffffffff) + if (*entry_addr == 0) *entry_addr = seghdr.addr; if (offset + seghdr.size > sz) break; @@ -340,10 +340,13 @@ int cx18_firmware_init(struct cx18 *cx) /* Only if the processor is not running */ if (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) { - u32 fw_entry_addr; + 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); -- cgit v1.2.3 From ee2d64f5ccc71b5c5191e92ea91a12b65f9ca060 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 16 Nov 2008 01:38:19 -0300 Subject: V4L/DVB (9720): cx18: Major rewrite of interrupt handling for incoming mailbox processing A major rewrite of interrupt handling for incoming mailbox processing, to split the timing critical steps from the the deferrable steps as the sending XPU on the CX23418 will time out and overwrite our incoming mailboxes rather quickly. Setup a pool of work "order forms" for the irq handler to send jobs to the new work handler routine which uses the kernel default work queue to do the deferrable work. Started optimizing some of the cx18-io calls as they are now the low hanging fruit for recoving microseconds back from the timeline. Future optimizations will get rid of mmio read retries, mmio stats logging, and combine smaller functions in the irq path into the larger ones to save ~2 us each. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-driver.c | 8 +- drivers/media/video/cx18/cx18-driver.h | 19 +- drivers/media/video/cx18/cx18-dvb.c | 25 --- drivers/media/video/cx18/cx18-dvb.h | 1 - drivers/media/video/cx18/cx18-firmware.c | 5 + drivers/media/video/cx18/cx18-io.c | 35 ---- drivers/media/video/cx18/cx18-io.h | 7 +- drivers/media/video/cx18/cx18-irq.c | 133 ++----------- drivers/media/video/cx18/cx18-irq.h | 2 - drivers/media/video/cx18/cx18-mailbox.c | 318 ++++++++++++++++++++++++++++++- drivers/media/video/cx18/cx18-mailbox.h | 16 +- drivers/media/video/cx18/cx18-queue.c | 25 ++- drivers/media/video/cx18/cx18-queue.h | 2 +- drivers/media/video/cx18/cx18-scb.h | 8 +- drivers/media/video/cx18/cx18-streams.c | 18 ++ drivers/media/video/cx18/cx18-streams.h | 1 + 16 files changed, 408 insertions(+), 215 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 8ef11d578b8d..434cc7fdee36 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -440,6 +440,8 @@ 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); @@ -451,7 +453,11 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) spin_lock_init(&cx->lock); - 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; diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index ce76806759ec..5ebf84b78ca8 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -203,8 +203,6 @@ 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 */ @@ -247,6 +245,19 @@ struct cx18_dvb { struct cx18; /* forward reference */ 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 */ + +struct cx18_epu_work_order { + struct work_struct work; + atomic_t pending; + struct cx18 *cx; + 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 { @@ -388,7 +399,6 @@ struct cx18 { 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; /* codec settings */ @@ -441,7 +451,8 @@ struct cx18 { /* when the current DMA is finished this queue is woken up */ wait_queue_head_t dma_waitq; - 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]; diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c index 4845f732ef48..b74253b1377e 100644 --- a/drivers/media/video/cx18/cx18-dvb.c +++ b/drivers/media/video/cx18/cx18-dvb.c @@ -23,8 +23,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" @@ -305,26 +303,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_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)) - 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 bbdcefc87f28..bf8d8f6f5455 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-firmware.c b/drivers/media/video/cx18/cx18-firmware.c index d9c5f55ab17c..fdea4b75204d 100644 --- a/drivers/media/video/cx18/cx18-firmware.c +++ b/drivers/media/video/cx18/cx18-firmware.c @@ -121,6 +121,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,6 +132,7 @@ 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; } @@ -150,6 +152,7 @@ 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; } @@ -185,6 +188,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; } } @@ -196,6 +200,7 @@ 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); + cx18_setup_page(cx, 0); return size; } diff --git a/drivers/media/video/cx18/cx18-io.c b/drivers/media/video/cx18/cx18-io.c index 3c6485fceea0..c67694f63d0e 100644 --- a/drivers/media/video/cx18/cx18-io.c +++ b/drivers/media/video/cx18/cx18-io.c @@ -166,41 +166,6 @@ u8 cx18_readb_retry(struct cx18 *cx, const void __iomem *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; diff --git a/drivers/media/video/cx18/cx18-io.h b/drivers/media/video/cx18/cx18-io.h index 4486b73faf5b..fdc2bcc92fca 100644 --- a/drivers/media/video/cx18/cx18-io.h +++ b/drivers/media/video/cx18/cx18-io.h @@ -249,8 +249,13 @@ static inline u32 cx18_write_sync(struct cx18 *cx, u32 val, void __iomem *addr) } +static inline void cx18_memcpy_fromio(struct cx18 *cx, void *to, - const void __iomem *from, unsigned int len); + const void __iomem *from, unsigned int len) +{ + memcpy_fromio(to, from, len); +} + void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count); diff --git a/drivers/media/video/cx18/cx18-irq.c b/drivers/media/video/cx18/cx18-irq.c index 4912b3c8e6a5..bc36a6b8f775 100644 --- a/drivers/media/video/cx18/cx18-irq.c +++ b/drivers/media/video/cx18/cx18-irq.c @@ -21,121 +21,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_HANDLER_DVB, &cx->i_flags)) - cx18_dvb_work_handler(cx); -} - -static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb, int rpu) -{ - 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, rpu); - 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, rpu); - wake_up(&cx->dma_waitq); - if (s->id != -1) - wake_up(&s->waitq); -} - -static void epu_debug(struct cx18 *cx, struct cx18_mailbox *mb, int rpu) -{ - 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, rpu); - 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, CPU); - break; - case CX18_EPU_DEBUG: - epu_debug(cx, &mb, CPU); - 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); - } -} static void xpu_ack(struct cx18 *cx, u32 sw2) { @@ -145,6 +33,14 @@ static void xpu_ack(struct cx18 *cx, u32 sw2) wake_up(&cx->mb_apu_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; @@ -170,6 +66,13 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id) 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)) { } @@ -178,11 +81,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)) - schedule_work(&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 6f3ec8963762..cd8f548b7ee8 100644 --- a/drivers/media/video/cx18/cx18-irq.h +++ b/drivers/media/video/cx18/cx18-irq.h @@ -32,5 +32,3 @@ #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 35f7188d4d3b..3d210d2ffdad 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -26,6 +26,10 @@ #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 */ @@ -92,12 +96,149 @@ static const struct cx18_api_info *find_api_info(u32 cmd) return NULL; } -long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb, int rpu) +static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name) +{ + char argstr[MAX_MB_ARGUMENTS*11+1]; + char *p; + int i; + + if (!(cx18_debug & CX18_DBGFLG_API)) + return; + + 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); +} + + +/* + * Functions that run in a work_queue work handling context + */ + +static void epu_dma_done(struct cx18 *cx, struct cx18_epu_work_order *order) +{ + u32 handle, mdl_ack_count; + 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\n", handle); + return; + } + + mdl_ack_count = mb->args[2]; + mdl_ack = order->mdl_ack; + for (i = 0; i < mdl_ack_count; i++, mdl_ack++) { + buf = cx18_queue_get_buf(s, mdl_ack->id, mdl_ack->data_used); + CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, + mdl_ack->id); + if (buf == NULL) { + CX18_WARN("Could not find buf %d for stream %s\n", + mdl_ack->id, s->name); + 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); + + 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); + } + wake_up(&cx->dma_waitq); + if (s->id != -1) + wake_up(&s->waitq); +} + +static void epu_debug(struct cx18 *cx, struct cx18_epu_work_order *order) +{ + char *p; + char *str = order->str; + + 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); +} + +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, const struct cx18_epu_work_order *order) { struct cx18_mailbox __iomem *ack_mb; - u32 ack_irq; + u32 ack_irq, req; - switch (rpu) { + switch (order->rpu) { case APU: ack_irq = IRQ_EPU_TO_APU_ACK; ack_mb = &cx->scb->apu2epu_mb; @@ -108,16 +249,175 @@ long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb, int rpu) break; default: CX18_WARN("Unhandled RPU (%d) for command %x ack\n", - rpu, mb->cmd); - return -EINVAL; + 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)) + 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, + int stale) +{ + 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 (!stale) + 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 (!stale) + mb_ack_irq(cx, order); + return 1; +} + +static +int epu_debug_irq(struct cx18 *cx, struct cx18_epu_work_order *order, int stale) +{ + 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 (!stale) + 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 stale) +{ + int ret = -1; + + switch (order->rpu) { + case CPU: + { + switch (order->mb.cmd) { + case CX18_EPU_DMA_DONE: + ret = epu_dma_done_irq(cx, order, stale); + break; + case CX18_EPU_DEBUG: + ret = epu_debug_irq(cx, order, stale); + 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 stale = 0; + 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->rpu = rpu; + order_mb = &order->mb; + cx18_memcpy_fromio(cx, order_mb, mb, sizeof(struct cx18_mailbox)); + + if (order_mb->request == order_mb->ack) { + CX18_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"); + stale = 1; + } + + /* + * Individual EPU command processing is responsible for ack-ing + * a non-stale mailbox as soon as possible + */ + submit = epu_cmd_irq(cx, order, stale); + if (submit > 0) { + schedule_work(&order->work); + } +} + + +/* + * Functions called from a non-interrupt, non work_queue context + */ + static void cx18_api_log_ack_delay(struct cx18 *cx, int msecs) { if (msecs > CX18_MAX_MB_ACK_DELAY) @@ -167,8 +467,6 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) } mutex_lock(mb_lock); - cx18_setup_page(cx, SCB_OFFSET); - /* * Wait for an in-use mailbox to complete * diff --git a/drivers/media/video/cx18/cx18-mailbox.h b/drivers/media/video/cx18/cx18-mailbox.h index 54758f32db1e..002c0526afa0 100644 --- a/drivers/media/video/cx18/cx18-mailbox.h +++ b/drivers/media/video/cx18/cx18-mailbox.h @@ -37,6 +37,17 @@ 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 { @@ -73,6 +84,9 @@ int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], u32 cmd, 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, int rpu); + +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 174682c2582f..c5a81aed61bb 100644 --- a/drivers/media/video/cx18/cx18-queue.c +++ b/drivers/media/video/cx18/cx18-queue.c @@ -75,30 +75,37 @@ struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q) 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; + unsigned long flags = 0; - spin_lock(&s->qlock); + spin_lock_irqsave(&s->qlock, flags); list_for_each(p, &s->q_free.list) { struct cx18_buffer *buf = list_entry(p, struct cx18_buffer, list); - if (buf->id != id) + if (buf->id != id) { + CX18_DEBUG_HI_DMA("Skipping buffer %d searching for %d " + "in stream %s q_free\n", buf->id, id, + s->name); 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); + if (s->type != CX18_ENC_STREAM_TYPE_TS) { + 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); + } - spin_unlock(&s->qlock); + spin_unlock_irqrestore(&s->qlock, flags); return buf; } - spin_unlock(&s->qlock); + spin_unlock_irqrestore(&s->qlock, flags); CX18_ERR("Cannot find buffer %d for stream %s\n", id, s->name); return NULL; } diff --git a/drivers/media/video/cx18/cx18-queue.h b/drivers/media/video/cx18/cx18-queue.h index 7f93bb13c09f..92edb5e3a627 100644 --- a/drivers/media/video/cx18/cx18-queue.h +++ b/drivers/media/video/cx18/cx18-queue.h @@ -46,7 +46,7 @@ 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.h b/drivers/media/video/cx18/cx18-scb.h index 594713bbed68..29866f02f018 100644 --- a/drivers/media/video/cx18/cx18-scb.h +++ b/drivers/media/video/cx18/cx18-scb.h @@ -85,12 +85,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 +270,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 c7d431f49238..e6d808f7cc8f 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -595,3 +595,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 f327e947b24f..3fd578d0e6ce 100644 --- a/drivers/media/video/cx18/cx18-streams.h +++ b/drivers/media/video/cx18/cx18-streams.h @@ -22,6 +22,7 @@ */ 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); -- cgit v1.2.3 From 18b5dc2ed7f0ede825dd1f93fefc7a61aba866e3 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 16 Nov 2008 17:15:01 -0300 Subject: V4L/DVB (9721): cx18: Change to singlethreaded global work queue thread for deferable work Change to singlethreaded global work queue thread for deferable work, instead of the kernel default multithreaded work queue. This ensures execution of deferable work is always in the proper order, so caputred buffers don't get reordered. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-driver.c | 25 ++++++++++++++++++++++++- drivers/media/video/cx18/cx18-driver.h | 1 + drivers/media/video/cx18/cx18-mailbox.c | 2 +- 3 files changed, 26 insertions(+), 2 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 434cc7fdee36..752ca908ccb1 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -56,6 +56,9 @@ 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, @@ -920,6 +923,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); @@ -937,7 +947,7 @@ static void cx18_remove(struct pci_dev *pci_dev) cx18_halt_firmware(cx); - flush_scheduled_work(); + cx18_cancel_epu_work_orders(cx); cx18_streams_cleanup(cx, 1); @@ -981,8 +991,17 @@ 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"); @@ -995,11 +1014,15 @@ 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; 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 5ebf84b78ca8..f8929eb72ad0 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -486,6 +486,7 @@ 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==================*/ diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index 3d210d2ffdad..d49c7c27c18f 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -409,7 +409,7 @@ void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu) */ submit = epu_cmd_irq(cx, order, stale); if (submit > 0) { - schedule_work(&order->work); + queue_work(cx18_work_queue, &order->work); } } -- cgit v1.2.3 From f576ceefb481e5617ecfb77e3a05b3d26dbf2f92 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 16 Nov 2008 20:18:05 -0300 Subject: V4L/DVB (9722): cx18: Convert per stream queue spinlocks into mutexes Convert the per stream queue spinlocks into mutexes. All queue manipulation happens via the work queue or system calls into the driver, and not in an interrupt context. This reduces the amout of time the cx18 driver keeps interrupts disabled. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-driver.h | 2 +- drivers/media/video/cx18/cx18-queue.c | 23 +++++++++-------------- drivers/media/video/cx18/cx18-streams.c | 2 +- 3 files changed, 11 insertions(+), 16 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index f8929eb72ad0..041fa660a7c2 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -271,7 +271,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 diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c index c5a81aed61bb..5a3839403631 100644 --- a/drivers/media/video/cx18/cx18-queue.c +++ b/drivers/media/video/cx18/cx18-queue.c @@ -44,34 +44,31 @@ void cx18_queue_init(struct cx18_queue *q) void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, struct cx18_queue *q) { - unsigned long flags = 0; - /* clear the buffer if it is going to be enqueued to the free queue */ if (q == &s->q_free) { buf->bytesused = 0; buf->readpos = 0; buf->b_flags = 0; } - spin_lock_irqsave(&s->qlock, flags); + mutex_lock(&s->qlock); list_add_tail(&buf->list, &q->list); atomic_inc(&q->buffers); q->bytesused += buf->bytesused - buf->readpos; - spin_unlock_irqrestore(&s->qlock, flags); + mutex_unlock(&s->qlock); } 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); q->bytesused -= buf->bytesused - buf->readpos; } - spin_unlock_irqrestore(&s->qlock, flags); + mutex_unlock(&s->qlock); return buf; } @@ -80,9 +77,8 @@ struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id, { struct cx18 *cx = s->cx; struct list_head *p; - unsigned long flags = 0; - spin_lock_irqsave(&s->qlock, flags); + mutex_lock(&s->qlock); list_for_each(p, &s->q_free.list) { struct cx18_buffer *buf = list_entry(p, struct cx18_buffer, list); @@ -102,10 +98,10 @@ struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id, list_move_tail(&buf->list, &s->q_full.list); } - spin_unlock_irqrestore(&s->qlock, flags); + mutex_unlock(&s->qlock); return buf; } - spin_unlock_irqrestore(&s->qlock, flags); + mutex_unlock(&s->qlock); CX18_ERR("Cannot find buffer %d for stream %s\n", id, s->name); return NULL; } @@ -113,13 +109,12 @@ struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id, /* 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); @@ -127,7 +122,7 @@ static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q) 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) diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index e6d808f7cc8f..e218e4d0ebf4 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -132,7 +132,7 @@ static void cx18_stream_init(struct cx18 *cx, int type) 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); -- cgit v1.2.3 From 72a4f8081af1c53a1673c173ce0fdd85c4b7d403 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 16 Nov 2008 21:18:00 -0300 Subject: V4L/DVB (9723): cx18: Propagate staleness of mailbox and mdl ack data to work handler cx18: Propagate staleness of mailbox and mdl ack data to work handler to let the work handler know that the data from the encoder may not be coherent. Allows for smarter handling of buffers in future, to deal with MDLs that fall out of rotation due to irq handler being late in collecting mailbox and mdl ack info. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-driver.h | 7 +++++++ drivers/media/video/cx18/cx18-mailbox.c | 34 +++++++++++++++++++-------------- 2 files changed, 27 insertions(+), 14 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index 041fa660a7c2..749bbb60a292 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -245,13 +245,20 @@ struct cx18_dvb { struct cx18; /* forward reference */ 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_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]; diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index d49c7c27c18f..844a62de6535 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -233,7 +233,7 @@ void cx18_epu_work_handler(struct work_struct *work) * Functions that run in an interrupt handling context */ -static void mb_ack_irq(struct cx18 *cx, const struct cx18_epu_work_order *order) +static void mb_ack_irq(struct cx18 *cx, struct cx18_epu_work_order *order) { struct cx18_mailbox __iomem *ack_mb; u32 ack_irq, req; @@ -256,15 +256,20 @@ static void mb_ack_irq(struct cx18 *cx, const struct cx18_epu_work_order *order) 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)) + req == cx18_readl(cx, &ack_mb->ack)) { + CX18_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; } -static int epu_dma_done_irq(struct cx18 *cx, struct cx18_epu_work_order *order, - int stale) +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; @@ -276,20 +281,21 @@ static int epu_dma_done_irq(struct cx18 *cx, struct cx18_epu_work_order *order, if (handle == CX18_INVALID_TASK_HANDLE || mdl_ack_count == 0 || mdl_ack_count > CX18_MAX_MDL_ACKS) { - if (!stale) + 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 (!stale) + + 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, int stale) +int epu_debug_irq(struct cx18 *cx, struct cx18_epu_work_order *order) { u32 str_offset; char *str = order->str; @@ -303,14 +309,14 @@ int epu_debug_irq(struct cx18 *cx, struct cx18_epu_work_order *order, int stale) cx18_setup_page(cx, SCB_OFFSET); } - if (!stale) + 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 stale) +int epu_cmd_irq(struct cx18 *cx, struct cx18_epu_work_order *order) { int ret = -1; @@ -319,10 +325,10 @@ int epu_cmd_irq(struct cx18 *cx, struct cx18_epu_work_order *order, int stale) { switch (order->mb.cmd) { case CX18_EPU_DMA_DONE: - ret = epu_dma_done_irq(cx, order, stale); + ret = epu_dma_done_irq(cx, order); break; case CX18_EPU_DEBUG: - ret = epu_debug_irq(cx, order, stale); + ret = epu_debug_irq(cx, order); break; default: CX18_WARN("Unknown CPU to EPU mailbox command %#0x\n", @@ -370,7 +376,6 @@ 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 stale = 0; int submit; switch (rpu) { @@ -391,6 +396,7 @@ void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu) return; } + order->flags = 0; order->rpu = rpu; order_mb = &order->mb; cx18_memcpy_fromio(cx, order_mb, mb, sizeof(struct cx18_mailbox)); @@ -400,14 +406,14 @@ void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu) " %s to EPU mailbox (sequence no. %u)\n", rpu_str[rpu], rpu_str[rpu], order_mb->request); dump_mb(cx, order_mb, "incoming"); - stale = 1; + 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, stale); + submit = epu_cmd_irq(cx, order); if (submit > 0) { queue_work(cx18_work_queue, &order->work); } -- cgit v1.2.3 From 3f75c6161f28e6a17c547daf552c1127c805c5e7 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 16 Nov 2008 23:33:41 -0300 Subject: V4L/DVB (9724): cx18: Streamline cx18-io[ch] wrappers and enforce MMIO retry strategy cx18: Streamline cx18-io[ch] wrappers and enforce MMIO retry strategy so that write retries always occur and read retries never occur (as they never help). Remove MMIO statistics logging to speed up MMIO accesses. Deprecate & ignore retry_mmio and mmio_ndelay module parameters, to essentially force retry_mmio=1 and mmio_ndelay=0. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-av-core.c | 5 - drivers/media/video/cx18/cx18-av-core.h | 1 - drivers/media/video/cx18/cx18-av-firmware.c | 5 +- drivers/media/video/cx18/cx18-driver.c | 28 +-- drivers/media/video/cx18/cx18-driver.h | 13 -- drivers/media/video/cx18/cx18-gpio.c | 2 - drivers/media/video/cx18/cx18-i2c.c | 18 +- drivers/media/video/cx18/cx18-io.c | 128 ----------- drivers/media/video/cx18/cx18-io.h | 320 +++++----------------------- 9 files changed, 71 insertions(+), 449 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c index 518bd701d393..13a4adaa1004 100644 --- a/drivers/media/video/cx18/cx18-av-core.c +++ b/drivers/media/video/cx18/cx18-av-core.c @@ -80,11 +80,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) { diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h index a07988c6f5cd..455761fce2fd 100644 --- a/drivers/media/video/cx18/cx18-av-core.h +++ b/drivers/media/video/cx18/cx18-av-core.h @@ -307,7 +307,6 @@ 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 924691dcaeb7..cf52e08c2878 100644 --- a/drivers/media/video/cx18/cx18-av-firmware.c +++ b/drivers/media/video/cx18/cx18-av-firmware.c @@ -68,8 +68,7 @@ int cx18_av_loadfw(struct cx18 *cx) 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 @@ -80,8 +79,6 @@ 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_WR_RETRIES) break; } diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 752ca908ccb1..88ce1e831221 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -78,14 +78,9 @@ 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[] = "-"; @@ -99,18 +94,20 @@ static int enc_pcm_buffers = CX18_DEFAULT_ENC_PCM_BUFFERS; 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); @@ -155,13 +152,11 @@ 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)); + "(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)); @@ -378,11 +373,6 @@ static void cx18_process_options(struct cx18 *cx) 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"); diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index 749bbb60a292..02a82c3b7a32 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -64,9 +64,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 @@ -176,7 +173,6 @@ #define CX18_MAX_PGM_INDEX (400) -extern int cx18_retry_mmio; /* enable check & retry of mmio accesses */ extern int cx18_debug; @@ -185,7 +181,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 */ @@ -371,13 +366,6 @@ struct cx18_i2c_algo_callback_data { }; #define CX18_MAX_MMIO_WR_RETRIES 10 -#define CX18_MAX_MMIO_RD_RETRIES 2 - -struct cx18_mmio_stats { - atomic_t retried_write[CX18_MAX_MMIO_WR_RETRIES+1]; - atomic_t retried_read[CX18_MAX_MMIO_RD_RETRIES+1]; -}; - #define CX18_MAX_MB_ACK_DELAY 100 struct cx18_mbox_stats { @@ -475,7 +463,6 @@ struct cx18 { struct mutex gpio_lock; /* Statistics */ - struct cx18_mmio_stats mmio_stats; struct cx18_mbox_stats mbox_stats; /* v4l2 and User settings */ diff --git a/drivers/media/video/cx18/cx18-gpio.c b/drivers/media/video/cx18/cx18-gpio.c index 17b7a32fcc31..a1625c8e0179 100644 --- a/drivers/media/video/cx18/cx18-gpio.c +++ b/drivers/media/video/cx18/cx18-gpio.c @@ -60,8 +60,6 @@ static void gpio_write(struct cx18 *cx) 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); - if (!cx18_retry_mmio) - (void) cx18_read_reg(cx, CX18_REG_GPIO_OUT2); /* sync */ } void cx18_reset_i2c_slaves_gpio(struct cx18 *cx) diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c index 824efbecb34c..098635072543 100644 --- a/drivers/media/video/cx18/cx18-i2c.c +++ b/drivers/media/video/cx18/cx18-i2c.c @@ -161,9 +161,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) @@ -174,9 +174,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) @@ -405,16 +405,10 @@ int init_cx18_i2c(struct cx18 *cx) } /* courtesy of Steven Toth */ cx18_write_reg_expect(cx, 0x00c00000, 0xc7001c, 0x00000000, 0x00c000c0); - if (!cx18_retry_mmio) - (void) cx18_read_reg(cx, 0xc7001c); /* sync */ mdelay(10); cx18_write_reg_expect(cx, 0x00c000c0, 0xc7001c, 0x000000c0, 0x00c000c0); - if (!cx18_retry_mmio) - (void) cx18_read_reg(cx, 0xc7001c); /* sync */ mdelay(10); cx18_write_reg_expect(cx, 0x00c00000, 0xc7001c, 0x00000000, 0x00c000c0); - if (!cx18_retry_mmio) - (void) cx18_read_reg(cx, 0xc7001c); /* sync */ mdelay(10); /* Set to edge-triggered intrs. */ @@ -424,12 +418,12 @@ int init_cx18_i2c(struct cx18 *cx) ~(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 c67694f63d0e..a2b5e807faca 100644 --- a/drivers/media/video/cx18/cx18-io.c +++ b/drivers/media/video/cx18/cx18-io.c @@ -31,12 +31,6 @@ void cx18_log_statistics(struct cx18 *cx) if (!(cx18_debug & CX18_DBGFLG_INFO)) return; - for (i = 0; i <= CX18_MAX_MMIO_WR_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_RD_RETRIES; i++) - CX18_DEBUG_INFO("retried_read[%d] = %d\n", i, - atomic_read(&cx->mmio_stats.retried_read[i])); for (i = 0; i <= CX18_MAX_MB_ACK_DELAY; i++) if (atomic_read(&cx->mbox_stats.mb_ack_delay[i])) CX18_DEBUG_INFO("mb_ack_delay[%d] = %d\n", i, @@ -44,128 +38,6 @@ void cx18_log_statistics(struct cx18 *cx) return; } -void cx18_raw_writel_retry(struct cx18 *cx, u32 val, void __iomem *addr) -{ - int i; - for (i = 0; i < CX18_MAX_MMIO_WR_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_RD_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_RD_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_WR_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_WR_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_WR_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_WR_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_RD_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_RD_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_RD_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_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count) { u8 __iomem *dst = addr; diff --git a/drivers/media/video/cx18/cx18-io.h b/drivers/media/video/cx18/cx18-io.h index fdc2bcc92fca..73321fb4cbf5 100644 --- a/drivers/media/video/cx18/cx18-io.h +++ b/drivers/media/video/cx18/cx18-io.h @@ -25,230 +25,118 @@ #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 . * The implmentation is the fault of Andy Walls . + * + * *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_WR_RETRIES) - i = CX18_MAX_MMIO_WR_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) -{ - if (i > CX18_MAX_MMIO_RD_RETRIES) - i = CX18_MAX_MMIO_RD_RETRIES; - atomic_inc(&cx->mmio_stats.retried_read[i]); - return; -} void cx18_log_statistics(struct cx18 *cx); /* Non byteswapping memory mapped IO */ +static inline u32 cx18_raw_readl(struct cx18 *cx, const void __iomem *addr) +{ + return __raw_readl(addr); +} + 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; + 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)) + 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); + if (val == cx18_readb(cx, addr)) + break; + } } - -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); -} - -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) -{ - if (cx18_retry_mmio) - return cx18_write_sync_retry(cx, val, addr); - - return cx18_write_sync_noretry(cx, val, addr); -} - - static inline void cx18_memcpy_fromio(struct cx18 *cx, void *to, const void __iomem *from, unsigned int len) @@ -265,130 +153,32 @@ 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); -- cgit v1.2.3 From d6c7e5f8faad080e75bace5c4f2265e3513e3510 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Mon, 17 Nov 2008 22:48:46 -0300 Subject: V4L/DVB (9725): cx18: Remove unnecessary MMIO accesses in time critical irq handling path Remove unnecessary MMIO accesses in time critical irq handling path. Also ensured that the mailbox ack field is read in last, so we know for sure if we have a stale mailbox or not on receipt. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-driver.h | 4 ++++ drivers/media/video/cx18/cx18-io.c | 20 ++++++++------------ drivers/media/video/cx18/cx18-irq.c | 13 ++++--------- drivers/media/video/cx18/cx18-mailbox.c | 7 ++++++- 4 files changed, 22 insertions(+), 22 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index 02a82c3b7a32..cad352aeb837 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -446,6 +446,10 @@ struct cx18 { /* 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 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 */ diff --git a/drivers/media/video/cx18/cx18-io.c b/drivers/media/video/cx18/cx18-io.c index a2b5e807faca..c6f1d0d7f2c2 100644 --- a/drivers/media/video/cx18/cx18-io.c +++ b/drivers/media/video/cx18/cx18-io.c @@ -71,32 +71,28 @@ 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) { - u32 r; - 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_cpu(struct cx18 *cx, u32 val) diff --git a/drivers/media/video/cx18/cx18-irq.c b/drivers/media/video/cx18/cx18-irq.c index bc36a6b8f775..2a94e50ad0a8 100644 --- a/drivers/media/video/cx18/cx18-irq.c +++ b/drivers/media/video/cx18/cx18-irq.c @@ -44,16 +44,11 @@ static void epu_cmd(struct cx18 *cx, u32 sw1) 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); diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index 844a62de6535..e5d4f3112293 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -399,7 +399,12 @@ void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu) order->flags = 0; order->rpu = rpu; order_mb = &order->mb; - cx18_memcpy_fromio(cx, order_mb, mb, sizeof(struct cx18_mailbox)); + + /* 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_WARN("Possibly falling behind: %s self-ack'ed our incoming" -- cgit v1.2.3 From bca11a5721917d6d5874571813673a2669ffec4b Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Wed, 19 Nov 2008 01:24:33 -0300 Subject: V4L/DVB (9726): cx18: Restore buffers that have fallen out of the transfer rotation Restore buffers that have fallen out of the transfer rotation, and check for coherent mailbox data when processing a stale mailbox. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-driver.h | 1 + drivers/media/video/cx18/cx18-mailbox.c | 59 +++++++++++++++++++++++++-------- drivers/media/video/cx18/cx18-queue.c | 59 +++++++++++++++++++++++++-------- 3 files changed, 92 insertions(+), 27 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index cad352aeb837..f06290d32ecf 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -212,6 +212,7 @@ struct cx18_buffer { dma_addr_t dma_handle; u32 id; unsigned long b_flags; + unsigned skipped; char *buf; u32 bytesused; diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index e5d4f3112293..abd39aaa345c 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -120,7 +120,7 @@ static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name) static void epu_dma_done(struct cx18 *cx, struct cx18_epu_work_order *order) { - u32 handle, mdl_ack_count; + u32 handle, mdl_ack_count, id; struct cx18_mailbox *mb; struct cx18_mdl_ack *mdl_ack; struct cx18_stream *s; @@ -133,19 +133,50 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_epu_work_order *order) if (s == NULL) { CX18_WARN("Got DMA done notification for unknown/inactive" - " handle %d\n", handle); + " handle %d, %s mailbox seq no %d\n", handle, + (order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) ? + "stale" : "good", mb->request); return; } mdl_ack_count = mb->args[2]; mdl_ack = order->mdl_ack; for (i = 0; i < mdl_ack_count; i++, mdl_ack++) { - buf = cx18_queue_get_buf(s, mdl_ack->id, mdl_ack->data_used); - CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, - mdl_ack->id); + 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 put them back in 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", - mdl_ack->id, s->name); + id, s->name); continue; } @@ -158,6 +189,7 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_epu_work_order *order) 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)) @@ -257,10 +289,10 @@ static void mb_ack_irq(struct cx18 *cx, struct cx18_epu_work_order *order) /* 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_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); + 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; } @@ -407,9 +439,10 @@ void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu) 2 * sizeof(u32)); if (order_mb->request == order_mb->ack) { - CX18_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); + 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; } diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c index 5a3839403631..ff6df36328fd 100644 --- a/drivers/media/video/cx18/cx18-queue.c +++ b/drivers/media/video/cx18/cx18-queue.c @@ -49,6 +49,7 @@ void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, buf->bytesused = 0; buf->readpos = 0; buf->b_flags = 0; + buf->skipped = 0; } mutex_lock(&s->qlock); list_add_tail(&buf->list, &q->list); @@ -67,6 +68,7 @@ struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q) list_del_init(q->list.next); atomic_dec(&q->buffers); q->bytesused -= buf->bytesused - buf->readpos; + buf->skipped = 0; } mutex_unlock(&s->qlock); return buf; @@ -76,34 +78,63 @@ struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id, u32 bytesused) { struct cx18 *cx = s->cx; - struct list_head *p; + struct cx18_buffer *buf; + struct cx18_buffer *ret = NULL; + struct list_head *p, *t; + LIST_HEAD(r); mutex_lock(&s->qlock); - list_for_each(p, &s->q_free.list) { - struct cx18_buffer *buf = - list_entry(p, struct cx18_buffer, list); + list_for_each_safe(p, t, &s->q_free.list) { + buf = list_entry(p, struct cx18_buffer, list); if (buf->id != id) { - CX18_DEBUG_HI_DMA("Skipping buffer %d searching for %d " - "in stream %s q_free\n", buf->id, id, - s->name); + buf->skipped++; + if (buf->skipped >= atomic_read(&s->q_free.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); + } continue; } buf->bytesused = bytesused; - if (s->type != CX18_ENC_STREAM_TYPE_TS) { - atomic_dec(&s->q_free.buffers); + atomic_dec(&s->q_free.buffers); + 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. + */ + list_del_init(&buf->list); + } else { atomic_inc(&s->q_full.buffers); s->q_full.bytesused += buf->bytesused; list_move_tail(&buf->list, &s->q_full.list); } - mutex_unlock(&s->qlock); - return buf; + ret = buf; + break; } mutex_unlock(&s->qlock); - CX18_ERR("Cannot find buffer %d for stream %s\n", id, s->name); - return NULL; + + /* 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; } /* Move all buffers of a queue to q_free, while flushing the buffers */ @@ -118,7 +149,7 @@ static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q) 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->bytesused = buf->readpos = buf->b_flags = buf->skipped = 0; atomic_inc(&s->q_free.buffers); } cx18_queue_init(q); -- cgit v1.2.3 From 2bb49f1b9f6a4f50222bc8a6b1e9df87a432c52c Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sat, 22 Nov 2008 01:23:22 -0300 Subject: V4L/DVB (9727): cx18: Adjust outgoing mailbox timeouts and remove statistics logging cx18: Adjust outgoing mailbox timeouts and remove statistics logging. This saves some wasted storage in struct cx18 for each card. Cutting the outgoing mailbox timeouts in half from the previous value appears to be safe with MythTV. Got rid of interrupted case code path after a wait uninterruptable returns. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-driver.c | 2 -- drivers/media/video/cx18/cx18-driver.h | 8 -------- drivers/media/video/cx18/cx18-io.c | 14 -------------- drivers/media/video/cx18/cx18-io.h | 4 ---- drivers/media/video/cx18/cx18-ioctl.c | 1 - drivers/media/video/cx18/cx18-mailbox.c | 33 ++++++++------------------------- 6 files changed, 8 insertions(+), 54 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 88ce1e831221..fbcbb500ca74 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -834,7 +834,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); @@ -951,7 +950,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); } diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index f06290d32ecf..6e4c90e6cc7c 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -367,11 +367,6 @@ struct cx18_i2c_algo_callback_data { }; #define CX18_MAX_MMIO_WR_RETRIES 10 -#define CX18_MAX_MB_ACK_DELAY 100 - -struct cx18_mbox_stats { - atomic_t mb_ack_delay[CX18_MAX_MB_ACK_DELAY+1]; -}; /* Struct to hold info about cx18 cards */ struct cx18 { @@ -467,9 +462,6 @@ struct cx18 { u32 gpio_val; struct mutex gpio_lock; - /* Statistics */ - struct cx18_mbox_stats mbox_stats; - /* v4l2 and User settings */ /* codec settings */ diff --git a/drivers/media/video/cx18/cx18-io.c b/drivers/media/video/cx18/cx18-io.c index c6f1d0d7f2c2..ec5b3d7bcc6b 100644 --- a/drivers/media/video/cx18/cx18-io.c +++ b/drivers/media/video/cx18/cx18-io.c @@ -24,20 +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_MB_ACK_DELAY; i++) - if (atomic_read(&cx->mbox_stats.mb_ack_delay[i])) - CX18_DEBUG_INFO("mb_ack_delay[%d] = %d\n", i, - atomic_read(&cx->mbox_stats.mb_ack_delay[i])); - return; -} - void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count) { u8 __iomem *dst = addr; diff --git a/drivers/media/video/cx18/cx18-io.h b/drivers/media/video/cx18/cx18-io.h index 73321fb4cbf5..e6716dcb1e8b 100644 --- a/drivers/media/video/cx18/cx18-io.h +++ b/drivers/media/video/cx18/cx18-io.h @@ -34,10 +34,6 @@ * *read* functions never retry the mmio (it never helps to do so) */ -/* Statistics gathering */ - -void cx18_log_statistics(struct cx18 *cx); - /* Non byteswapping memory mapped IO */ static inline u32 cx18_raw_readl(struct cx18 *cx, const void __iomem *addr) { diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index f0ca50f5fdde..a0e667362cb5 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -752,7 +752,6 @@ static int cx18_log_status(struct file *file, void *fh) 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-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index abd39aaa345c..79647c6d6c55 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -462,13 +462,6 @@ void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu) * Functions called from a non-interrupt, non work_queue context */ -static void cx18_api_log_ack_delay(struct cx18 *cx, int msecs) -{ - if (msecs > CX18_MAX_MB_ACK_DELAY) - msecs = CX18_MAX_MB_ACK_DELAY; - atomic_inc(&cx->mbox_stats.mb_ack_delay[msecs]); -} - static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) { const struct cx18_api_info *info = find_api_info(cmd); @@ -523,7 +516,7 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) */ state = cx18_readl(cx, xpu_state); req = cx18_readl(cx, &mb->request); - timeout = msecs_to_jiffies(20); /* 1 field at 50 Hz vertical refresh */ + timeout = msecs_to_jiffies(10); ret = wait_event_timeout(*waitq, (ack = cx18_readl(cx, &mb->ack)) == req, timeout); @@ -533,8 +526,8 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) 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 usecs for busy mbox to be acked\n", - jiffies_to_usecs(timeout-ret)); + 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; @@ -548,10 +541,8 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) /* * Notify the XPU and wait for it to send an Ack back - * 21 ms = ~ 0.5 frames at a frame rate of 24 fps - * 42 ms = ~ 1 frame at a frame rate of 24 fps */ - timeout = msecs_to_jiffies((info->flags & API_FAST) ? 21 : 42); + 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); @@ -561,27 +552,19 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) *waitq, cx18_readl(cx, &mb->ack) == cx18_readl(cx, &mb->request), timeout); + if (ret == 0) { /* Timed out */ mutex_unlock(mb_lock); - i = jiffies_to_msecs(timeout); - cx18_api_log_ack_delay(cx, i); CX18_WARN("sending %s timed out waiting %d msecs for RPU " - "acknowledgement\n", info->name, i); + "acknowledgement\n", + info->name, jiffies_to_msecs(timeout)); return -EINVAL; - } else if (ret < 0) { - /* Interrupted */ - mutex_unlock(mb_lock); - CX18_WARN("sending %s was interrupted waiting for RPU" - "acknowledgement\n", info->name); - return -EINTR; } - i = jiffies_to_msecs(timeout-ret); - cx18_api_log_ack_delay(cx, i); if (ret != timeout) CX18_DEBUG_HI_API("waited %u msecs for %s to be acked\n", - i, info->name); + jiffies_to_msecs(timeout-ret), info->name); /* Collect data returned by the XPU */ for (i = 0; i < MAX_MB_ARGUMENTS; i++) -- cgit v1.2.3 From 1ed9dcc8ef61c35a620fecc039c01f2c50dceb80 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sat, 22 Nov 2008 01:37:34 -0300 Subject: V4L/DVB (9728): cx18: Copyright attribution update for files modified by awalls Add copyright attribution for files modified by awalls in 2008 Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-av-audio.c | 1 + drivers/media/video/cx18/cx18-av-core.c | 1 + drivers/media/video/cx18/cx18-av-core.h | 1 + drivers/media/video/cx18/cx18-av-firmware.c | 1 + drivers/media/video/cx18/cx18-cards.c | 1 + drivers/media/video/cx18/cx18-cards.h | 1 + drivers/media/video/cx18/cx18-driver.h | 1 + drivers/media/video/cx18/cx18-dvb.c | 1 + drivers/media/video/cx18/cx18-fileops.c | 1 + drivers/media/video/cx18/cx18-firmware.c | 1 + drivers/media/video/cx18/cx18-gpio.c | 1 + drivers/media/video/cx18/cx18-gpio.h | 1 + drivers/media/video/cx18/cx18-i2c.c | 1 + drivers/media/video/cx18/cx18-ioctl.c | 1 + drivers/media/video/cx18/cx18-ioctl.h | 1 + drivers/media/video/cx18/cx18-irq.c | 1 + drivers/media/video/cx18/cx18-irq.h | 1 + drivers/media/video/cx18/cx18-mailbox.c | 1 + drivers/media/video/cx18/cx18-mailbox.h | 1 + drivers/media/video/cx18/cx18-queue.c | 1 + drivers/media/video/cx18/cx18-queue.h | 1 + drivers/media/video/cx18/cx18-scb.c | 1 + drivers/media/video/cx18/cx18-scb.h | 1 + drivers/media/video/cx18/cx18-streams.c | 1 + drivers/media/video/cx18/cx18-streams.h | 1 + 25 files changed, 25 insertions(+) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-av-audio.c b/drivers/media/video/cx18/cx18-av-audio.c index 486cad0c26e0..fd85b9b2d241 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 + * Copyright (C) 2008 Andy Walls * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c index 13a4adaa1004..40ea6fde6922 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 + * Copyright (C) 2008 Andy Walls * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h index 455761fce2fd..cf68a6039091 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 + * Copyright (C) 2008 Andy Walls * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/drivers/media/video/cx18/cx18-av-firmware.c b/drivers/media/video/cx18/cx18-av-firmware.c index cf52e08c2878..c64fd0a05a97 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 + * Copyright (C) 2008 Andy Walls * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c index 5efe01ebe9db..fb9120d95f98 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 + * Copyright (C) 2008 Andy Walls * * 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-cards.h b/drivers/media/video/cx18/cx18-cards.h index 32155f6e6fe4..a54aae9ed702 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 + * Copyright (C) 2008 Andy Walls * * 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-driver.h b/drivers/media/video/cx18/cx18-driver.h index 6e4c90e6cc7c..ca1f43781941 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 + * Copyright (C) 2008 Andy Walls * * 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-dvb.c b/drivers/media/video/cx18/cx18-dvb.c index b74253b1377e..034e09a372f1 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 + * Copyright (C) 2008 Andy Walls * * 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-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index 5f9089907544..2c8117510fb4 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 + * Copyright (C) 2008 Andy Walls * * 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-firmware.c b/drivers/media/video/cx18/cx18-firmware.c index fdea4b75204d..8eac84314d40 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 + * Copyright (C) 2008 Andy Walls * * 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-gpio.c b/drivers/media/video/cx18/cx18-gpio.c index a1625c8e0179..1a99329f33cb 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 + * Copyright (C) 2008 Andy Walls * * 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-gpio.h b/drivers/media/video/cx18/cx18-gpio.h index beb7424b9944..39ffccc19d8a 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 + * Copyright (C) 2008 Andy Walls * * 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 098635072543..8941f58bed7f 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 + * Copyright (C) 2008 Andy Walls * * 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-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index a0e667362cb5..8b2ec31b9421 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 + * Copyright (C) 2008 Andy Walls * * 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-ioctl.h b/drivers/media/video/cx18/cx18-ioctl.h index 2222f679d86d..08fe24e9510e 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 + * Copyright (C) 2008 Andy Walls * * 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 2a94e50ad0a8..af2f504eda2b 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 + * Copyright (C) 2008 Andy Walls * * 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.h b/drivers/media/video/cx18/cx18-irq.h index cd8f548b7ee8..91f0b5278ef9 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 + * Copyright (C) 2008 Andy Walls * * 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-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index 79647c6d6c55..70802a948a10 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 + * Copyright (C) 2008 Andy Walls * * 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-mailbox.h b/drivers/media/video/cx18/cx18-mailbox.h index 002c0526afa0..33d5a0e7080b 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 + * Copyright (C) 2008 Andy Walls * * 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-queue.c b/drivers/media/video/cx18/cx18-queue.c index ff6df36328fd..7b09c9a3ee8f 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 + * Copyright (C) 2008 Andy Walls * * 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-queue.h b/drivers/media/video/cx18/cx18-queue.h index 92edb5e3a627..ff50a2b7e5bc 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 + * Copyright (C) 2008 Andy Walls * * 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-scb.c b/drivers/media/video/cx18/cx18-scb.c index f56d3772aa67..ac18bd9326d5 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 + * Copyright (C) 2008 Andy Walls * * 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-scb.h b/drivers/media/video/cx18/cx18-scb.h index 29866f02f018..1dc1c431f5a1 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 + * Copyright (C) 2008 Andy Walls * * 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-streams.c b/drivers/media/video/cx18/cx18-streams.c index e218e4d0ebf4..8c8e92b20c7b 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 + * Copyright (C) 2008 Andy Walls * * 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-streams.h b/drivers/media/video/cx18/cx18-streams.h index 3fd578d0e6ce..7218b15045bf 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 + * Copyright (C) 2008 Andy Walls * * 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 -- cgit v1.2.3 From 20543ec414dee5e9d5e7a82f678fa2c0b21c5bdb Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sat, 22 Nov 2008 01:40:11 -0300 Subject: V4L/DVB (9729): cx18: Update version due to significant irq handling changes Update the version number due to significant changes in the way the mailboxes and interrupts are handled. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h index 366cc14729d7..eb043d599455 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 2 +#define CX18_DRIVER_VERSION_PATCHLEVEL 3 #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, \ -- cgit v1.2.3 From ec984f437842426622fb54b56bbcc416183eefe6 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 23 Nov 2008 16:27:57 -0300 Subject: V4L/DVB (9730): cx18: Quiet a sometimes common warning that often has benign consequences cx18: Quiet a sometimes common warning that often has benign consequences. No one probably cares that the firmware took forever to ack our command, as they always seem to succeed whether or not the firmware acks it in a reasonable amount of time. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-mailbox.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index 70802a948a10..70a82721b551 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -557,9 +557,9 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) if (ret == 0) { /* Timed out */ mutex_unlock(mb_lock); - CX18_WARN("sending %s timed out waiting %d msecs for RPU " - "acknowledgement\n", - info->name, jiffies_to_msecs(timeout)); + CX18_DEBUG_WARN("sending %s timed out waiting %d msecs for RPU " + "acknowledgement\n", + info->name, jiffies_to_msecs(timeout)); return -EINVAL; } -- cgit v1.2.3 From 572bfea71b0fb2efb36407b4e284c1e7962d4779 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Tue, 25 Nov 2008 21:43:05 -0300 Subject: V4L/DVB (9776): cx18: Change to per CX23418 device work queues for deferrable work handling cx18: Change to per CX23418 device work queues for deferrable work handling. Needed to support 2.6.22 and earlier kernels that can't selectively cancel work orders. Also will provide slightly better performance on SMP systems. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-driver.c | 33 ++++++++++++--------------------- drivers/media/video/cx18/cx18-driver.h | 2 +- drivers/media/video/cx18/cx18-mailbox.c | 2 +- 3 files changed, 14 insertions(+), 23 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index fbcbb500ca74..a893caff0aa9 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/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, @@ -446,6 +443,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; @@ -655,12 +658,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); @@ -830,6 +830,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; @@ -938,6 +939,8 @@ static void cx18_remove(struct pci_dev *pci_dev) cx18_cancel_epu_work_orders(cx); + destroy_workqueue(cx->work_queue); + cx18_streams_cleanup(cx, 1); exit_cx18_i2c(cx); @@ -979,17 +982,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"); @@ -1002,9 +996,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/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index ca1f43781941..94c196a91295 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -447,6 +447,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 +479,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==================*/ diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index 70a82721b551..b013e817926a 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -454,7 +454,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); } } -- cgit v1.2.3 From daa1c164db63540fe7a52c658ce14ae6a8d1ae53 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 30 Nov 2008 10:01:21 -0300 Subject: V4L/DVB (9778): cx18: cx18_writel_expect() should not declare success on a PCI read error cx18: cx18_writel_expect() should not declare success on a PCI read error. This removes the potential for cx18_write*_expect() calls to not accomplish a PCI write successfully as expected. The CX18-AV core uses the *expect() calls often and this may be the source of intermittent audio problems and standands switching problems. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-io.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-io.h b/drivers/media/video/cx18/cx18-io.h index e6716dcb1e8b..2635b3a8cc96 100644 --- a/drivers/media/video/cx18/cx18-io.h +++ b/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; } } -- cgit v1.2.3 From b80e1074c734416987486b7b76b6479faa73f1e2 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Fri, 28 Nov 2008 00:04:21 -0300 Subject: V4L/DVB (9800): cx18: Eliminate q_io from stream buffer handling Eliminate q_io from stream buffer handling in anticipation of upcoming changes in buffer handling. q_io was a holdover from ivtv and it's function in cx18 was trivial and not necessary. We just push things back onto the front of q_full now, instead of maintaining a 1 buffer q_io queue. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-driver.h | 1 - drivers/media/video/cx18/cx18-fileops.c | 9 ++------- drivers/media/video/cx18/cx18-queue.c | 10 ++++++---- drivers/media/video/cx18/cx18-queue.h | 19 +++++++++++++++++-- drivers/media/video/cx18/cx18-streams.c | 1 - 5 files changed, 25 insertions(+), 15 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index 94c196a91295..f88d82348293 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -290,7 +290,6 @@ 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 */ /* DVB / Digital Transport */ struct cx18_dvb dvb; diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index 2c8117510fb4..504a1f9edec6 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/drivers/media/video/cx18/cx18-fileops.c @@ -195,11 +195,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) { @@ -375,7 +370,7 @@ static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf, cx->enc_mem, 1, buf->id, s->buf_size); } else - cx18_enqueue(s, buf, &s->q_io); + cx18_push(s, buf, &s->q_full); } else if (buf->readpos == buf->bytesused) { int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES; @@ -519,7 +514,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-queue.c b/drivers/media/video/cx18/cx18-queue.c index 7b09c9a3ee8f..fdfc83ee3d4e 100644 --- a/drivers/media/video/cx18/cx18-queue.c +++ b/drivers/media/video/cx18/cx18-queue.c @@ -42,8 +42,8 @@ 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) +void _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) { @@ -53,7 +53,10 @@ void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, buf->skipped = 0; } mutex_lock(&s->qlock); - list_add_tail(&buf->list, &q->list); + if (to_front) + list_add(&buf->list, &q->list); /* LIFO */ + else + list_add_tail(&buf->list, &q->list); /* FIFO */ atomic_inc(&q->buffers); q->bytesused += buf->bytesused - buf->readpos; mutex_unlock(&s->qlock); @@ -159,7 +162,6 @@ 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_full); } diff --git a/drivers/media/video/cx18/cx18-queue.h b/drivers/media/video/cx18/cx18-queue.h index ff50a2b7e5bc..d184e55ce624 100644 --- a/drivers/media/video/cx18/cx18-queue.h +++ b/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 */ -void cx18_queue_init(struct cx18_queue *q); +void _cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, + struct cx18_queue *q, int to_front); + +static inline void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, - struct cx18_queue *q); + struct cx18_queue *q) +{ + _cx18_enqueue(s, buf, q, 0); /* FIFO */ +} + +static inline +void cx18_push(struct cx18_stream *s, struct cx18_buffer *buf, + struct cx18_queue *q) +{ + _cx18_enqueue(s, buf, q, 1); /* LIFO */ +} + +void cx18_queue_init(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/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 8c8e92b20c7b..a1cf434f7975 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -138,7 +138,6 @@ static void cx18_stream_init(struct cx18 *cx, int type) s->id = -1; cx18_queue_init(&s->q_free); cx18_queue_init(&s->q_full); - cx18_queue_init(&s->q_io); } static int cx18_prep_dev(struct cx18 *cx, int type) -- cgit v1.2.3 From 66c2a6b0bc0b394d215768610d96f44cf97052ac Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Mon, 8 Dec 2008 23:02:45 -0300 Subject: V4L/DVB (9801): cx18: Allow more than 63 capture buffers in rotation per stream cx18: Allow more than 63 capture buffers in rotation per stream. Implement q_busy to hold buffers the firmware has for use. q_free holds truly unused buffers in a pool. New buffers are given to the firmware as soon as the firmware returns one, if there are any to give to the firmware. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-driver.h | 3 +- drivers/media/video/cx18/cx18-fileops.c | 14 ++---- drivers/media/video/cx18/cx18-ioctl.c | 3 +- drivers/media/video/cx18/cx18-mailbox.c | 11 +---- drivers/media/video/cx18/cx18-queue.c | 56 ++++++++++++------------ drivers/media/video/cx18/cx18-queue.h | 16 +++---- drivers/media/video/cx18/cx18-streams.c | 77 ++++++++++++++++++++++++++++----- drivers/media/video/cx18/cx18-streams.h | 3 ++ 8 files changed, 113 insertions(+), 70 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index f88d82348293..4d56d07195b6 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -289,7 +289,8 @@ struct cx18_stream { /* Buffer Queues */ struct cx18_queue q_free; /* free buffers */ - struct cx18_queue q_full; /* full buffers */ + 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; diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index 504a1f9edec6..61192e62a80f 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/drivers/media/video/cx18/cx18-fileops.c @@ -187,7 +187,7 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, 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_stream_put_buf_fw(s_vbi, buf); } } buf = &cx->vbi.sliced_mpeg_buf; @@ -361,15 +361,9 @@ 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 + 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; diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index 8b2ec31b9421..ece799ec37aa 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -746,8 +746,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", diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index b013e817926a..9d8220539be8 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -189,16 +189,7 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_epu_work_order *order) 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); + cx18_stream_put_buf_fw(s, buf); } else set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags); } diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c index fdfc83ee3d4e..40379d807cef 100644 --- a/drivers/media/video/cx18/cx18-queue.c +++ b/drivers/media/video/cx18/cx18-queue.c @@ -42,24 +42,32 @@ 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, int to_front) +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); + + /* q_busy is restricted to 63 buffers to stay within firmware limits */ + if (q == &s->q_busy && atomic_read(&q->buffers) >= 63) + q = &s->q_free; + if (to_front) list_add(&buf->list, &q->list); /* LIFO */ else list_add_tail(&buf->list, &q->list); /* FIFO */ - atomic_inc(&q->buffers); 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) @@ -70,9 +78,9 @@ struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q) 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); q->bytesused -= buf->bytesused - buf->readpos; buf->skipped = 0; + atomic_dec(&q->buffers); } mutex_unlock(&s->qlock); return buf; @@ -85,28 +93,30 @@ struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id, struct cx18_buffer *buf; 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) { + list_for_each_safe(p, t, &s->q_busy.list) { buf = list_entry(p, struct cx18_buffer, 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); if (s->type == CX18_ENC_STREAM_TYPE_TS) { /* * TS doesn't use q_full, but for sweeping up lost @@ -116,28 +126,19 @@ struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id, */ list_del_init(&buf->list); } else { - atomic_inc(&s->q_full.buffers); - s->q_full.bytesused += buf->bytesused; list_move_tail(&buf->list, &s->q_full.list); + 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... */ - } + /* Put more buffers into the transfer rotation from q_free, if we can */ + cx18_stream_load_fw_queue_nolock(s); + mutex_unlock(&s->qlock); return ret; } @@ -162,6 +163,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_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 d184e55ce624..456cec3bc28f 100644 --- a/drivers/media/video/cx18/cx18-queue.h +++ b/drivers/media/video/cx18/cx18-queue.h @@ -43,21 +43,21 @@ static inline void cx18_buf_sync_for_device(struct cx18_stream *s, void cx18_buf_swap(struct cx18_buffer *buf); /* cx18_queue utility functions */ -void _cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, - struct cx18_queue *q, int to_front); +struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, + struct cx18_queue *q, int to_front); static inline -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) { - _cx18_enqueue(s, buf, q, 0); /* FIFO */ + return _cx18_enqueue(s, buf, q, 0); /* FIFO */ } static inline -void cx18_push(struct cx18_stream *s, struct cx18_buffer *buf, - struct cx18_queue *q) +struct cx18_queue *cx18_push(struct cx18_stream *s, struct cx18_buffer *buf, + struct cx18_queue *q) { - _cx18_enqueue(s, buf, q, 1); /* LIFO */ + return _cx18_enqueue(s, buf, q, 1); /* LIFO */ } void cx18_queue_init(struct cx18_queue *q); diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index a1cf434f7975..6b0b7f751f20 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -127,16 +127,11 @@ static void cx18_stream_init(struct cx18 *cx, int 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); } @@ -401,11 +396,61 @@ 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; +} + +/* Must hold s->qlock when calling */ +void cx18_stream_load_fw_queue_nolock(struct cx18_stream *s) +{ + struct cx18_buffer *buf; + struct cx18 *cx = s->cx; + + /* Move from q_free to q_busy notifying the firmware: 63 buf limit */ + while (s->handle != CX18_INVALID_TASK_HANDLE && + test_bit(CX18_F_S_STREAMING, &s->s_flags) && + atomic_read(&s->q_busy.buffers) < 63 && + !list_empty(&s->q_free.list)) { + + /* Move from q_free to q_busy */ + buf = list_entry(s->q_free.list.next, struct cx18_buffer, list); + list_move_tail(&buf->list, &s->q_busy.list); + buf->bytesused = buf->readpos = buf->b_flags = buf->skipped = 0; + atomic_dec(&s->q_free.buffers); + atomic_inc(&s->q_busy.buffers); + + /* Notify firmware */ + 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); + } +} + 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; @@ -488,16 +533,18 @@ 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); + /* Init all the cpu_mdls for this stream */ + cx18_flush_queues(s); + mutex_lock(&s->qlock); list_for_each(p, &s->q_free.list) { - struct cx18_buffer *buf = list_entry(p, struct cx18_buffer, list); - + buf = list_entry(p, struct cx18_buffer, 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); } + cx18_stream_load_fw_queue_nolock(s); + mutex_unlock(&s->qlock); + /* begin_capture */ if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) { CX18_DEBUG_WARN("Error starting capture!\n"); @@ -506,9 +553,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/drivers/media/video/cx18/cx18-streams.h b/drivers/media/video/cx18/cx18-streams.h index 7218b15045bf..635d34b75ab1 100644 --- a/drivers/media/video/cx18/cx18-streams.h +++ b/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_nolock(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); -- cgit v1.2.3 From 6ecd86dcc838fa446ec86334a9fe0c1e415e3514 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 7 Dec 2008 23:30:17 -0300 Subject: V4L/DVB (9802): cx18: Add module parameters for finer control over buffer allocations cx18: Add module parameters for finer control over buffer allocations. User now has the option of setting smaller buffers to get lower latency transfers from the encoder. User can also now set the number of buffers used for a stream explicitly. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-driver.c | 154 +++++++++++++++++++++++++++++--- drivers/media/video/cx18/cx18-driver.h | 12 +++ drivers/media/video/cx18/cx18-dvb.c | 4 + drivers/media/video/cx18/cx18-streams.c | 21 ++--- 4 files changed, 167 insertions(+), 24 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index a893caff0aa9..255d5477567d 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -83,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; @@ -108,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, @@ -154,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"); @@ -361,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]; @@ -768,13 +889,18 @@ 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; 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; diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index 4d56d07195b6..29c296f39f91 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/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_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 @@ -408,6 +419,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 */ diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c index 034e09a372f1..bd5e6f3fd4d0 100644 --- a/drivers/media/video/cx18/cx18-dvb.c +++ b/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/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 6b0b7f751f20..d2690ccdf327 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -111,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)); @@ -124,9 +123,9 @@ 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; + mutex_init(&s->qlock); init_waitqueue_head(&s->waitq); s->id = -1; @@ -162,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; } @@ -262,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: @@ -272,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); -- cgit v1.2.3 From 765f6f612ef69ada79f7ec2627dcbc49276bf7b5 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Mon, 8 Dec 2008 23:14:46 -0300 Subject: V4L/DVB (9803): cx18: Increment version number due to siginificant buffering changes cx18: Increment version number due to siginificant buffering changes. Now version 1.0.4 Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h index eb043d599455..84c0ff13b607 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 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, \ -- cgit v1.2.3 From abb096de82f6f920a06ca935f76925261e66b556 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Fri, 12 Dec 2008 15:50:27 -0300 Subject: V4L/DVB (9804): cx18: Avoid making firmware API calls with the queue lock held cx18: Avoid making firmware API calls with the queue lock held. The source of MPEG strem corruption when not holding the queue lock was found to be that the MPEG buffer could be retrieved by the user app before it was sync'ed for the host cpu. Incoming buffers are now sync'ed before being put on q_full and releasing the queue lock. We can thus avoid the sometimes lengthy call to the firmware for CPU_DE_SET_MDL while holding the queue lock, so we can get better performance. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-mailbox.c | 15 +++++++------ drivers/media/video/cx18/cx18-queue.c | 13 ++++++------ drivers/media/video/cx18/cx18-streams.c | 37 +++++++++++++-------------------- drivers/media/video/cx18/cx18-streams.h | 2 +- 4 files changed, 30 insertions(+), 37 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index 9d8220539be8..ca8d5f4b731a 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -163,7 +163,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,24 +174,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); - + } + /* 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); - } else - set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags); } wake_up(&cx->dma_waitq); if (s->id != -1) diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c index 40379d807cef..a6b0666f0ba3 100644 --- a/drivers/media/video/cx18/cx18-queue.c +++ b/drivers/media/video/cx18/cx18-queue.c @@ -117,16 +117,18 @@ struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id, } buf->bytesused = bytesused; + /* 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 { + /* 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); } @@ -135,9 +137,6 @@ struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id, ret = buf; break; } - - /* Put more buffers into the transfer rotation from q_free, if we can */ - cx18_stream_load_fw_queue_nolock(s); mutex_unlock(&s->qlock); return ret; } diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index d2690ccdf327..9ead4591b1d2 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -419,31 +419,22 @@ struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s, return q; } -/* Must hold s->qlock when calling */ -void cx18_stream_load_fw_queue_nolock(struct cx18_stream *s) +void cx18_stream_load_fw_queue(struct cx18_stream *s) { + struct cx18_queue *q; struct cx18_buffer *buf; - struct cx18 *cx = s->cx; - /* Move from q_free to q_busy notifying the firmware: 63 buf limit */ - while (s->handle != CX18_INVALID_TASK_HANDLE && - test_bit(CX18_F_S_STREAMING, &s->s_flags) && - atomic_read(&s->q_busy.buffers) < 63 && - !list_empty(&s->q_free.list)) { - - /* Move from q_free to q_busy */ - buf = list_entry(s->q_free.list.next, struct cx18_buffer, list); - list_move_tail(&buf->list, &s->q_busy.list); - buf->bytesused = buf->readpos = buf->b_flags = buf->skipped = 0; - atomic_dec(&s->q_free.buffers); - atomic_inc(&s->q_busy.buffers); - - /* Notify firmware */ - 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); - } + if (atomic_read(&s->q_free.buffers) == 0 || + atomic_read(&s->q_busy.buffers) >= 63) + 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) < 63 && q == &s->q_busy); } int cx18_start_v4l2_encode_stream(struct cx18_stream *s) @@ -543,8 +534,8 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) &cx->scb->cpu_mdl[buf->id].paddr); cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length); } - cx18_stream_load_fw_queue_nolock(s); mutex_unlock(&s->qlock); + cx18_stream_load_fw_queue(s); /* begin_capture */ if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) { diff --git a/drivers/media/video/cx18/cx18-streams.h b/drivers/media/video/cx18/cx18-streams.h index 635d34b75ab1..420e0a172945 100644 --- a/drivers/media/video/cx18/cx18-streams.h +++ b/drivers/media/video/cx18/cx18-streams.h @@ -29,7 +29,7 @@ int cx18_streams_register(struct cx18 *cx); void cx18_streams_cleanup(struct cx18 *cx, int unregister); /* Capture related */ -void cx18_stream_load_fw_queue_nolock(struct cx18_stream *s); +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); -- cgit v1.2.3 From dd073434b5285121007860914a004320d644ee7e Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Fri, 12 Dec 2008 16:24:04 -0300 Subject: V4L/DVB (9805): cx18: Port fix for raw/sliced VBI mixup from ivtv and cx25840 This is a port of the fixes Hans Verkuil made for ivtv/cx25840: The service_set field was used to determine whether raw or sliced VBI was desired. This is incorrect since it is perfectly valid to select sliced VBI with a service_set of 0. Instead the driver should check on VIDIOC_S_FMT whether the type field matches the raw or sliced VBI type. Updated the cx18 driver accordingly, including an additional check in cx18_start_v4l2_encode_stream() that didn't exist in ivtv. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-av-vbi.c | 5 +++-- drivers/media/video/cx18/cx18-driver.c | 2 +- drivers/media/video/cx18/cx18-driver.h | 6 ++++++ drivers/media/video/cx18/cx18-fileops.c | 10 ++++------ drivers/media/video/cx18/cx18-ioctl.c | 7 +++---- drivers/media/video/cx18/cx18-streams.c | 6 +++--- drivers/media/video/cx18/cx18-vbi.c | 2 +- 7 files changed, 21 insertions(+), 17 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-av-vbi.c b/drivers/media/video/cx18/cx18-av-vbi.c index 02fdf57bb678..1527ea4f6b06 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-driver.c b/drivers/media/video/cx18/cx18-driver.c index 255d5477567d..e845cd653bb8 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -594,7 +594,7 @@ 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; diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index 29c296f39f91..018d98f94f97 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -504,4 +504,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-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index 61192e62a80f..1856d59e0cf6 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/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; @@ -258,7 +257,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; @@ -333,8 +332,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 (;;) { diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index ece799ec37aa..e6087486f889 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -238,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); } diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 9ead4591b1d2..c87cd5369a4f 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -340,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; @@ -471,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, diff --git a/drivers/media/video/cx18/cx18-vbi.c b/drivers/media/video/cx18/cx18-vbi.c index 22e76ee3f447..03f0e81307fa 100644 --- a/drivers/media/video/cx18/cx18-vbi.c +++ b/drivers/media/video/cx18/cx18-vbi.c @@ -160,7 +160,7 @@ 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); -- cgit v1.2.3 From af009cf635141858642864a26602e379e97bf7d6 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Fri, 12 Dec 2008 20:00:29 -0300 Subject: V4L/DVB (9806): cx18: Enable raw VBI capture A combined authorship patch from Hans Verkuil and Andy Walls. Raw VBI can now be captured but requires a video capture to be in progress as well. Signed-off-by: Andy Walls Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-cards.c | 8 +++--- drivers/media/video/cx18/cx18-cards.h | 5 ++-- drivers/media/video/cx18/cx18-driver.c | 49 ++++++++++++++++++++++++++++----- drivers/media/video/cx18/cx18-fileops.c | 4 ++- drivers/media/video/cx18/cx18-streams.c | 8 +++--- drivers/media/video/cx18/cx18-vbi.c | 3 ++ 6 files changed, 59 insertions(+), 18 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c index fb9120d95f98..e274043657dd 100644 --- a/drivers/media/video/cx18/cx18-cards.c +++ b/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/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h index a54aae9ed702..6fa7bcb42dde 100644 --- a/drivers/media/video/cx18/cx18-cards.h +++ b/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/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index e845cd653bb8..863ebf06a829 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -596,13 +596,47 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) /* VBI */ 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 NTSC: + * + * (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 PAL: + * + * (1/15,625 kHz) * 2 * 13.5 MHz = 1728 samples/line = + * 4 bytes SAV + 280 bytes anc data + 4 bytes SAV + 1440 active samples + * + */ + + /* 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; } @@ -635,6 +669,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; } diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index 1856d59e0cf6..425271a29517 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/drivers/media/video/cx18/cx18-fileops.c @@ -185,7 +185,9 @@ 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_process_vbi_data(cx, buf, + s_vbi->dma_pts, + s_vbi->type); cx18_stream_put_buf_fw(s_vbi, buf); } } diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index c87cd5369a4f..985bad89f311 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -358,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) { @@ -377,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; diff --git a/drivers/media/video/cx18/cx18-vbi.c b/drivers/media/video/cx18/cx18-vbi.c index 03f0e81307fa..fb595bd548e8 100644 --- a/drivers/media/video/cx18/cx18-vbi.c +++ b/drivers/media/video/cx18/cx18-vbi.c @@ -165,6 +165,9 @@ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, 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); -- cgit v1.2.3 From 0ef0289264e8f278312909f1639a7764cc9cf580 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 14 Dec 2008 18:52:12 -0300 Subject: V4L/DVB (9891): cx18 Replace magic number 63 with CX18_MAX_FW_MDLS_PER_STREAM Removed magic number that referred to firmware limit on the number of MDLs the firmware can maintain for any stream at any one time. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-driver.h | 5 +++-- drivers/media/video/cx18/cx18-queue.c | 5 +++-- drivers/media/video/cx18/cx18-streams.c | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index 018d98f94f97..0d2edebc39b4 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -116,7 +116,7 @@ #define CX18_DEFAULT_ENC_PCM_BUFFERS 1 /* Maximum firmware DMA buffers per stream */ -#define CX18_MAX_MDLS_PER_STREAM 63 +#define CX18_MAX_FW_MDLS_PER_STREAM 63 /* DMA buffer, default size in kB allocated */ #define CX18_DEFAULT_ENC_TS_BUFSIZE 32 @@ -255,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 diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c index a6b0666f0ba3..2c58b0c478c0 100644 --- a/drivers/media/video/cx18/cx18-queue.c +++ b/drivers/media/video/cx18/cx18-queue.c @@ -55,8 +55,9 @@ struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, mutex_lock(&s->qlock); - /* q_busy is restricted to 63 buffers to stay within firmware limits */ - if (q == &s->q_busy && atomic_read(&q->buffers) >= 63) + /* 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) diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 985bad89f311..8fc655d3ced9 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -425,7 +425,7 @@ void cx18_stream_load_fw_queue(struct cx18_stream *s) struct cx18_buffer *buf; if (atomic_read(&s->q_free.buffers) == 0 || - atomic_read(&s->q_busy.buffers) >= 63) + 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 */ @@ -434,7 +434,8 @@ void cx18_stream_load_fw_queue(struct cx18_stream *s) if (buf == NULL) break; q = cx18_stream_put_buf_fw(s, buf); - } while (atomic_read(&s->q_busy.buffers) < 63 && q == &s->q_busy); + } 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) -- cgit v1.2.3 From aafc77066f4cc8b6de4af1a7d643c9646e19bad9 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 14 Dec 2008 19:21:35 -0300 Subject: V4L/DVB (9892): cx18: VBI comment corrections and comments about VBI issues VBI comment corrections to avoid future confusion about standards. Comments on cx18 VBI implementation shortcomings that need resolution. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-driver.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 863ebf06a829..d7baf1b97cb8 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -609,18 +609,21 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) * 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 NTSC: + * 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 = + * (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 PAL: + * 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 = + * (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 */ @@ -924,6 +927,11 @@ static int __devinit cx18_probe(struct pci_dev *dev, } cx->params.video_gop_size = cx->is_60hz ? 15 : 12; + /* + * 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; -- cgit v1.2.3 From f6b181ac3bda7aab227e9ee3148bc9b7f1889c57 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 14 Dec 2008 21:05:36 -0300 Subject: V4L/DVB (9893): cx18: Convert some list manipulations to emphasize entries not lists Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-queue.c | 14 ++++++-------- drivers/media/video/cx18/cx18-streams.c | 4 +--- 2 files changed, 7 insertions(+), 11 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c index 2c58b0c478c0..8d9441e88c4e 100644 --- a/drivers/media/video/cx18/cx18-queue.c +++ b/drivers/media/video/cx18/cx18-queue.c @@ -77,8 +77,8 @@ 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); + 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); @@ -92,13 +92,11 @@ 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; mutex_lock(&s->qlock); - list_for_each_safe(p, t, &s->q_busy.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_busy.buffers)-1) { @@ -152,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); } diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 8fc655d3ced9..600a5fcac3d0 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -442,7 +442,6 @@ 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; @@ -529,8 +528,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) /* Init all the cpu_mdls for this stream */ cx18_flush_queues(s); mutex_lock(&s->qlock); - list_for_each(p, &s->q_free.list) { - buf = list_entry(p, struct cx18_buffer, list); + 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); -- cgit v1.2.3 From 50b86bac6ae6dda00faa14f7d73ae2412eacc240 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 23 Nov 2008 19:16:44 -0300 Subject: V4L/DVB (9894): cx18: Use a known open task handle when setting stream CX2341x parameters cx18: Use a known open task handle when setting stream CX2341x parameters Sometimes, we might only have VBI or YUV stream open instead of an MPEG stream. Let's make sure we use a valid task handle to perform the CX2341x control settings. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-controls.c | 5 ++++- drivers/media/video/cx18/cx18-mailbox.c | 5 +++-- drivers/media/video/cx18/cx18-mailbox.h | 7 +++++++ drivers/media/video/cx18/cx18-streams.c | 6 +++++- 4 files changed, 19 insertions(+), 4 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c index f46c7e5ed747..17edf305d649 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-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index ca8d5f4b731a..d62351466e26 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -606,8 +606,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 33d5a0e7080b..ce2b6686aa00 100644 --- a/drivers/media/video/cx18/cx18-mailbox.h +++ b/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/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 600a5fcac3d0..63c336c95ff5 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -494,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); @@ -513,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) { -- cgit v1.2.3 From fd6b9c978dc3447b9b4677d8949ef3ea7f946abc Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 14 Dec 2008 21:26:25 -0300 Subject: V4L/DVB (9895): cx18: Refine the firmware load and firmware startup process Refine the firmware load and firmware startup process. Significant changes are to ensure the SCB and IPC area are correct before starting up the firmware, and letting the CPU firmware start up the APU firmware for us. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-driver.c | 1 + drivers/media/video/cx18/cx18-firmware.c | 101 ++++++++++++++++--------------- drivers/media/video/cx18/cx18-mailbox.c | 3 +- drivers/media/video/cx18/cx18-scb.c | 1 - drivers/media/video/cx18/cx23418.h | 6 ++ 5 files changed, 60 insertions(+), 52 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index d7baf1b97cb8..f50cf2167adc 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -878,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, diff --git a/drivers/media/video/cx18/cx18-firmware.c b/drivers/media/video/cx18/cx18-firmware.c index 8eac84314d40..e74f76d47df5 100644 --- a/drivers/media/video/cx18/cx18-firmware.c +++ b/drivers/media/video/cx18/cx18-firmware.c @@ -332,6 +332,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 +345,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/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index d62351466e26..de5e723fdf44 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/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), }; diff --git a/drivers/media/video/cx18/cx18-scb.c b/drivers/media/video/cx18/cx18-scb.c index ac18bd9326d5..34b4d03c55cd 100644 --- a/drivers/media/video/cx18/cx18-scb.c +++ b/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/drivers/media/video/cx18/cx23418.h b/drivers/media/video/cx18/cx23418.h index 668f968d7761..601f3a2ab742 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_... */ -- cgit v1.2.3 From 57e24b629a5282bee24aaff44977270a3e462041 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sat, 20 Dec 2008 23:26:38 -0300 Subject: V4L/DVB (9936): cx18: Disable locking of Video and Audio PLL for analog captures Disable AV_LOCK, locking of audio PLL to video PLL in the cx18-av-core for analog captures. It seems to have adverse effects on captures from SVideo and CVBS sources with current clock & crystal settings in the driver. Many thanks to Jeff Campbell and Mike Bradley for reporting this problem, and suggesting the solution and performing extensive testing to support their suggestion. Reported-by: Jeff Campbell Reported-by: Mike Bradley Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-av-audio.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-av-audio.c b/drivers/media/video/cx18/cx18-av-audio.c index fd85b9b2d241..148f710974d5 100644 --- a/drivers/media/video/cx18/cx18-av-audio.c +++ b/drivers/media/video/cx18/cx18-av-audio.c @@ -57,11 +57,11 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq) 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: @@ -82,11 +82,11 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq) 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: @@ -107,11 +107,11 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq) 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 { @@ -141,11 +141,11 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq) 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: @@ -170,11 +170,11 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq) 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: @@ -199,11 +199,11 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq) 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; } } -- cgit v1.2.3 From 55d81aa5c11f9da510a72cc48fd59689b3e91e3a Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sat, 20 Dec 2008 23:48:57 -0300 Subject: V4L/DVB (9937): cx18: Use a consistent crystal value for computing all PLL parameters Use a consistent crystal value of 28.636360 MHz for computing all PLL parameters so clocks don't have relative error due to assumed crystal value mismatches. Also aimed to have all PLLs run their VOCs at close to 400 MHz to minimze the error of these PLLs as frequency synthesizers. Also set the VDCLK and AIMCLK PLLs to sane values before the APU and CPU firmware are loaded. Also fixed I2S Master clock dividers. Many thanks to Mike Bradley and Jeff Campbell for reporting this problem and suggesting the solution, researching and experimenting, and performing extensive testing to support their suggested solution. Reported-by: Jeff Campbell Reported-by: Mike Bradley Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-av-audio.c | 165 +++++++++++++++++++++++-------- drivers/media/video/cx18/cx18-av-core.c | 2 +- drivers/media/video/cx18/cx18-firmware.c | 69 ++++++++++++- 3 files changed, 191 insertions(+), 45 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-av-audio.c b/drivers/media/video/cx18/cx18-av-audio.c index 148f710974d5..a2f0ad570434 100644 --- a/drivers/media/video/cx18/cx18-av-audio.c +++ b/drivers/media/video/cx18/cx18-av-audio.c @@ -31,27 +31,67 @@ 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); @@ -65,19 +105,29 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq) 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); @@ -90,19 +140,29 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq) 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); @@ -117,12 +177,19 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq) } 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,8 +201,8 @@ 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); @@ -149,12 +216,19 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq) 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,6 +240,9 @@ 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); @@ -178,12 +255,19 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq) 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,6 +279,9 @@ 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); diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c index 40ea6fde6922..0b1c84b4ddd6 100644 --- a/drivers/media/video/cx18/cx18-av-core.c +++ b/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/drivers/media/video/cx18/cx18-firmware.c b/drivers/media/video/cx18/cx18-firmware.c index e74f76d47df5..1fa95da1575e 100644 --- a/drivers/media/video/cx18/cx18-firmware.c +++ b/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 #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 */ -- cgit v1.2.3